OmniAICreator/Qiita-1.07M huggingface dataset:
Railsのテーブル内要素を一括表示する方法（カラム名も含めて）

####【ページ内リンク】
[０．環境](#０環境)
[１．全表示](#１全表示)
[２．カラムを指定して表示](#２カラムを指定して表示)

#０．環境
・AWS
・Rails 5.2.4.5
・ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
・MySQL 5.7.31

以下、`User`テーブルの例とする
#１．全表示
```erb:example1.html.erb
<% @users = User.all %>

<table>
  <tr>
    <% @users.column_names.each do |column| %>
      <th><%= column %></th>
    <% end %>
  </tr>

  <% @users.each do |user| %>
    <tr>
      <!--☆☆☆-->
      <% user.attributes.values.each do |value| %>
        <td><%= value %></td>
      <% end %>
      <!--☆☆☆-->
    </tr>
  <% end %>

</table>
```

```:表示例1
id  name    created_at                  updated_at
0   admin   2021-03-09 13:00:21 +0900   2021-03-09 13:00:21 +0900
1   qqq     2021-03-09 13:01:21 +0900   2021-03-09 13:01:21 +0900
```
・`<table>`等は見やすくするために使っただけなので、無くてもOK
・`モデル名.column_names`は文字通りモデルの全カラム名を取得できる
・`user.attributes`はuserのカラムをハッシュにして返してくれる。でもuserに複数の値が含まれてたらエラーとなる。
・`~.value`はハッシュの値のみを取り出すために付けた。つまり`☆☆☆`部分は以下のように書き換え可能。

```erb:☆☆☆部分書き換え
      <% user.attributes.each do |hash| %>
        <td><%= hash[1] %></td> 
      <% end %>
```
・こっちの方がまだマシ･･･？

[ページ内リンクへ戻る](#ページ内リンク)

#２．カラムを指定して表示

```erb:example2.html.erb
<% @users = User.all %>
<!--↓表示させるカラムを指定-->
<% @user_columns = ["id", "name"] %>

<table>
  <tr>
    <% @user_columns.each do |column| %>
      <th><%= column %></th>
    <% end %>
  </tr>
  
  <% @users.each do |user| %>
    <tr>
      <% @user_columns.each do |column| %>
        <td><%= user[column] %></td>
      <% end %>
    </tr>
  <% end %>
</table>
```
```:表示例2
id  name    
0   admin  
1   qqq    
```
・`created_at`とか表示しなくて良いのでは･･･？と思ったので作成した
・カラム指定方法は他には、`<% @user_columns = @users.column_names[0,2] %>`など
・`<% @user_columns.each do |column| %>`が2回使われているのが気持ち悪いけど、今の自分じゃこれを1つにまとめる術が分からない･･･

[ページ内リンクへ戻る](#ページ内リンク)
[Backlog] APIのレート制限が既存処理に影響あるか確認する

Backlogから、[APIのレート制限を導入するというお知らせ](https://backlog.com/ja/blog/backlog-api-rate-limit-announcement/) が届きました。

詳細はリンク先に記載されていますが、概要をまとめると、

* APIに対して、 <b><font color="Blue">1分間あたりのリクエスト数制限</font></b> を導入する。
* 2021年1月25日以降に作成されたスペースには即導入、<b><font color="Blue">既存スペースは2021年7月末から制限</font></b> が適用される。
* 制限はユーザ単位、APIの種類単位で設定される。
    * <b><font color="Blue">APIキー単位ではなくユーザ単位で制限</font></b> され、同一ユーザ複数APIキーを使用している場合は相互に影響を受ける。
    * <b><font color="Blue">個別のAPI単位ではなくAPI種類で制限</font></b> され、有料プランの場合は 読み込みが 600回/分、更新が 150回/分、検索が 150回/分、アイコン取得 60回/分。
* 制限を超えた場合のレスポンスコードは <b><font color="Blue">429 : Too Many Requests</font></b> 。

そして、既存スペースが影響を受けるかどうかの確認手段としては

* 各APIのレスポンスヘッダ（ X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset ）での確認
* レート上限の取得API（ /api/v2/rateLimit ）

が提供されているので、それぞれ試してみました。

# 1) APIのレスポンスで確認

各APIに対して、レスポンスヘッダに項目が追加されています。

* X-RateLimit-Limit : 1分間に受付可能な最大リクエスト数
* X-RateLimit-Remaining	: X-RateLimit-Resetの時刻までに受付可能な残りリクエスト数
* X-RateLimit-Reset : リクエスト数の計測がリセットされる時刻（UNIX時間）

例として、プロジェクト一覧取得のAPIを実行してみると、以下のように結果が取得できました。

```
$ curl -s -D - https://＜スペース名＞.backlog.jp/api/v2/projects?apiKey=＜確認対象のAPIキー＞ \
  | grep X-RateLimit
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1615357273
```

このAPIを実行したことにより、残りリクエスト数が 1減っていることが確認できます。

レート制限に対する対応としては、これらを確認してのリクエスト数の調整、あるいは429エラーが返った場合のリトライ等の対応が必要となります。

# 2) レート上限の取得API（ /api/v2/rateLimit ）で確認

[レート上限を取得するAPI : /api/v2/rateLimit](https://developer.nulab.com/ja/docs/backlog/api/2/get-rate-limit/) も別に提供されています。

```
$ curl -s https://＜スペース名＞.backlog.jp/api/v2/rateLimit?apiKey=＜確認対象のAPIキー＞ \
  | jq -r .
{
  "rateLimit": {
    "read": {
      "limit": 600,
      "remaining": 598,
      "reset": 1615357067
    },
    "update": {
      "limit": 150,
      "remaining": 150,
      "reset": 1615357016
    },
    "search": {
      "limit": 150,
      "remaining": 150,
      "reset": 1615357016
    },
    "icon": {
      "limit": 60,
      "remaining": 60,
      "reset": 1615357016
    }
  }
}
```

結果にあるように、API種類ごとの制限がまとめて取得できます。下記は jqのフィルタ条件を追加し、残りリクエスト数のリストを取得する例です。

```
$ curl -s https://＜スペース名＞.backlog.jp/api/v2/rateLimit?apiKey=＜確認対象のAPIキー＞ \
  | jq -r '.rateLimit | [ .read.remaining , .update.remaining , .search.remaining , .icon.remaining ] | @csv'
599,150,150,60
```

Backlog APIには [各種のAPIライブラリ](https://developer.nulab.com/ja/docs/backlog/libraries/) もありますが、レスポンスヘッダの対応がまだされていないものもあるようです。

とり急ぎAPIを使った既存処理への影響を確認するためには、こちらのAPIを定期的(10秒間隔とか)に実行して傾向を見る方法も考えられます。ただし例にあったように、このAPI自身が参照系のリクエスト数を消費するので、そこは考慮に入れる必要があります。

# 参考資料

- [【重要なお知らせ】BacklogはAPIへの高頻度なリクエスト送信を制限します](https://backlog.com/ja/blog/backlog-api-rate-limit-announcement/)
- [レート上限の取得API : /api/v2/rateLimit](https://developer.nulab.com/ja/docs/backlog/api/2/get-rate-limit/)
- [Backlog APIライブラリ](https://developer.nulab.com/ja/docs/backlog/libraries/)
アウトプットのメリットとは？

## はじめに
この記事では、外部への発信（アウトプット）にどの様なメリットが存在するのかをまとめてみたいと思います。私自身外部への発信は定期的に行っているのですが、まだまだ外部への発信が活発にされていないなと感じる場面が多々あり、この記事を作成しようと思い立ちました。

この記事では以下の事柄について考えてみたいと思います。

* アウトプットの定義について。
* アウトプットを阻んでいる心理的な要因・障壁とは何か。そして、それに対する対策。
* 外部への発信にどの様なメリットがあるのか。

この記事によって、外部への発信をしたことがないエンジニアが少しでも発信するようになっていただければ幸いです。
至らぬ点も多々あるかと思いますが、間違いなどあればご指摘いただけると幸いです。

## アウトプットの定義
ここでひとまず、アウトプットの定義について少し考えてみます。

一言に「アウトプット」と言っても、様々な種類が存在します。エンジニアが普段から行って
いるコーディング・テスト仕様書の作成・ドキュメンテーション・メールの作成なども、自分の知識を外に出すという意味で広い意味でのアウトプットでしょう。

それを踏まえて、この記事ではアウトプットを以下の2種類に大別したいと思います。

### 閉じたアウトプット
まず、一つは自分の所属している組織・あるいは会社間などだけに公開される、閉じたアウトプット（**クローズドアウトプット**とここでは呼称します）があります。外部で発信されずにどこかの会社・組織間だけに限定されている成果物と捉えるとわかりやすいかもしれません。
具体的には仕事で行っているコーディングやドキュメンテーションなどはこちらに該当するかと思います。

### 開けたアウトプット
そして、もう一つには、世界に向けてのアウトプット（ここでは**オープンアウトプット**と呼称します）があります。
具体的には、Qiitaの記事の投稿、StackOverflowへの回答、OSSへのコントリビュートなどといった全世界に発信される様なアウトプットはこちらに該当します。

### この記事での定義
この記事では、**アウトプット＝オープンアウトプット**という意味で定義いたします。よって、世界に自分の知識・知見を公開する事によるメリットについて考察してゆけたらと思っています。

## アウトプットに対する心理的障壁
アウトプットへの定義が終わったところで、次にアウトプットすることに対する心理的な障壁とはなんでしょうか。少し考えてみたいと思います。

### ネガティブな反応が返ってきそう👿
これは確かに世界に向けて発信している以上、止むを得ないところですね。
コードレビューにおいてもコードについて指摘されただけなのに、人格まで否定されたような気分になる物です。

全く場違いな場合は無視、そして核心をつくような意見に関しては、それを受け止め、後学のためという認識を持って望むというのがベストかと思います。

### 知識不足（だと思っている）💧
自分にはアウトプットするだけの知識・知見・経験が無い、、と自分で思っているということもあります。
しかし、私は全てのエンジニアには共有できる知見や知識が備わっているのでは無いかと思っています。

今まで蓄えた知識・経験はもちろん人それぞれによって異なり、一つとして同じ物などありません。
既に世の中に出ている知識などをアウトプットするという、いわゆる"車輪の再発明"でも大丈夫だと思います。（もちろん他の人の記事を丸々コピーするなどといった行為はダメですが）

後述しますが、アウトプットをすることで様々なメリットが享受できます。ほんの些細な気づきでも、他のエンジニアの方からしたら全然解決できない不具合を解消できた！ということにも繋がります。

私自身、そういったアウトプットに何度も助けられました。

## アウトプットのメリットとは？
アウトプットのメリットについては、こちらの記事が大変参考になります。

https://qiita.com/hinora/items/ddf50293c35691470640

なので、少し繰り返しになってしまうかもしれないですが、ここではあえて外部への発信をするということについてのメリットについて考察してゆきたいと思います。

### 様々なプロフェッショナルからのフィードバックをもらえる
私はこれが一番のメリットかなと感じます。
外部へのアウトプットは、国内のみならず海外の方からもフィードバックをもらえたりすることもあります。
そういった様々な方の意見はもの凄く参考になりますし、新しい発見にも繋がります。そして、個人のスキル・技術力の底上げにも繋がります。

### 他社エンジニアとの交流
また、他社のエンジニアの方との交流にも繋がるかと感じます。
例えば、外部向けのLTなどでアウトプットする際にも話をする時の話題にも繋がりますし、交流するきっかけに繋がります。記事や投稿を読んでTwitter, Facebookなどをフォローするみたいな事にも繋がるのかなと感じます。

交流の幅が広くなると、フィードバックや意見をもらいやすくなったりもします。

### 調べる力・文章力がつく
これはどういうことかといいますと、外部へ発信する際には、少なからず他者の目を気にするかと思います。

要するに外部への発信をし、他の人からの目を気にする事によって、より丁寧に調べたり、丁寧に文章を作成する習慣が付くかと思っています。

自分の文章を推敲するのは、恥ずかしいと思うかもしれないですが、私は文章を書く時や、コードを書いてプッシュする際にも必ずセルフレビューを行うようにしています。その習慣は外部への記事の発信などで鍛えられたと思っています。

### 社外・社内からの評価
社外・社内からの評価ということもあるでしょう。
Qiitaなどで積極的にアウトプットをすれば、社内および社外でその姿勢が評価されることもあるかもしれません。

外部への発信が積極的に評価に繋がる様な風土が出来上がってくれないかな、と密かに思っていたりもします。

## まとめ
外部への発信という行為自体は、誰かから強制されてやる物ではありませんが、エンジニアのスキル・技術力などを底上げするためにとても役に立ちます。
そして、外部への発信は思いもよらない、自分では考えもつかなかった気づきを与えてくれることがあります。

一人でも多くの方が積極的にアウトプットをしていただけると幸いです。
Xcodeで作成したArchivesがOrganizerに表示されなくなった話(ReactNative)

ReactNativeアプリをリリースするために、XcodeのArchivesでIPAを作成してもOrganizerに表示されなくなった。
ワイはなにをしてしまったのか。。
なんとなくXcodeのエラー(？)から、`Cocoapods`のバージョンの話に辿り着くのがしんどかったので。

ちなみにアプリでは地図を表示するため`@react-native-mapbox-gl/maps`というライブラリを使用。あとCocoaPodsも。

# 環境
```
Xcode: Version 12.4
react-native: 0.63.4
cocoapods: 1.10.1

@react-native-mapbox-gl/maps: 8.1.0-rc10
```

# どういうこと
Archive実行後、[Window] -> [Organizer] を見てみると`No Archives` 😇
![Archiveない例.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85879/c6d0111d-7192-aad9-b6b3-377fe9ca2155.png)
なにが困るって、Archiveは正常に完了したように見えて、エラーログ等が出力されないのが困る。

ちなみに`/Users/(ユーザ)/Library/Developer/Xcode/Archives`を覗くと、作成した日時のディレクトリと、なんかできているので開いてみると、エラーぽいのが表示される（エラー内容は忘れた）。

# 解決
https://github.com/react-native-mapbox-gl/maps/issues/1097
こちらのissueを見ていると、「`CocoaPods`のバージョンを`1.9.3`まで下げたらいいゾ」というコメントが多かった。
たしかに直近で`CocoaPods`のバージョンを上げていた。。

## ダウングレード
```
$ gem list | grep pods
$ gem uninstall cocoapods  <- cocoapods-*系何個かいるのでそいつらも消した方がいいかも
$ gem install cocoapods -v 1.9.3
$ pod --version
-> 1.9.3
```

## podファイルの解決
```
$ rm -rf ios/Pods
$ rm -rf ios/Podfile.lock
$ cd ios
$ pod install
$ cd ..
```

## Archivesの再実行
![Archiveある例.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85879/8b873119-3ed1-3097-1dc7-2a686c629046.png)
わーい🙌

# 原因
もともと`react-native-mapbox-gl`になにかあるのかな、正直あんまり分かってないです。
`Cocoapods`のバージョンの違いで依存関係の解決方法に違いが出てたのかなーくらいの認識。
Vuetify~ダウンロード~

今回はJavaScriptのフレームワークであるVue.jsで使われるVuetifyを実際にダウンロードしてみました👍


まず、Vue.jsの開発ツールであるVue CLIをダウンロードします！
早速ターミナルを開き、下記のように打っていきます！

```
% npm install -g @vue/cli
% vue create  my-app
% cd my-app
```
新しく作られたappの中に入ります👍

```
% vue add vuetify
% vue --version
% vue ui
```
ダウンロードが終わりましたら、早速Vueitfyダウンロードを開始していきます！

```
% vue add vuetify
% npm run serve
```
これでダウンロード完了です。
サーバーを起動し、ブラウザ上で表示するとこうなります👌

![スクリーンショット 2021-03-10 15.01.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1033397/c0b0b9af-c39d-8669-1613-4849a0ce9034.png)
Java Wordで 透かしを削除

<p><strong>前はWordに透かし「ウォーターマーク」を追加する方法を紹介しました。今日は　透かしを削除する方法を紹介することにします。Spire.Doc for Javaというライブラリを使って簡単にできます。</strong></p>
<h4><strong>下準備</strong></h4>
<p><strong>1.E-iceblueの公式サイトから<a href="https://www.e-iceblue.com/Download/doc-for-java-free.html">Free Spire.doc for Java</a>無料版をダウンロードしてください。</strong></p>
<p><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/l/lendoris/20210310/20210310145949.png" alt="f:id:lendoris:20210310145949p:plain" title="" class="hatena-fotolife" itemprop="image" /></p>
<p><strong>2.IDEを起動して新規プロジェクトを作成してから、インストールされたファイルにあった相応しいSpire.doc.jarを参照に追加してください。</strong></p>
<p><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/l/lendoris/20210310/20210310150029.png" alt="f:id:lendoris:20210310150029p:plain" title="" class="hatena-fotolife" itemprop="image" /></p>
<h4><strong>透かし付きのファイル</strong></h4>
<p><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/l/lendoris/20210310/20210310150120.png" alt="f:id:lendoris:20210310150120p:plain" title="" class="hatena-fotolife" itemprop="image" /></p>
```JAVA
import com.spire.doc.Document;
import com.spire.doc.FileFormat;

public class RemoveWatermark {
    public static void main(String[] args){
       
        Document doc = new Document();
        //Wordをロードします。
        doc.loadFromFile("ImageWatermark.docx");

        //透かしを削除します。
        doc.setWatermark(null);

        //保存します。
        doc.saveToFile("RemoveWatermark.docx", FileFormat.Docx_2013);
    }
}
```
<h4> <strong>実行結果</strong></h4>
<p><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/l/lendoris/20210310/20210310150137.png" alt="f:id:lendoris:20210310150137p:plain" title="" class="hatena-fotolife" itemprop="image" /></p>
<p> </p>
【Salesforce認定資格試験問題】アドミニストレーター４

[⇐【Salesforce認定資格試験問題】アドミニストレーター３](https://qiita.com/TomAndJerry/items/c91d8f79c76d9e4bfdd7)

<strong>３１．Web-to-ケースを使用していますので、ウェブサイトを通じて提出されたサポートリクエストがサポートチームまで飛ばされています。件名に「パスワード」「忘れた」という言葉が含まれているケースに対して、パスワードリセット案内を含むメールを自動的に送信するには、管理者が何をすべきですか？
A.メール-to-ケース
B.自動レスポンスルール
C.入力規則
D.サポート設定

<details><summary>答え</summary>B.自動レスポンスルール
解説：
◇
自動レスポンスルールを使用すると、レコードの属性に基づいてリードまたはケース登録者にメールレスポンスを送信できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634241/6a6c4ee1-c831-e7c5-3754-61381853e6e6.png)
https://help.salesforce.com/articleView?id=sf.creating_auto-response_rules.htm&type=5
◇
Aについて、メール-to-ケースは、顧客からのメールを自動的にケースに変換する機能となり、ケース登録者へメールレスポンスを自動的に送信できません。
https://help.salesforce.com/articleView?id=sf.customizesupport_email.htm&type=5
◇
Dについて、サポート設定は、メールテンプレート、デフォルトのケース所有者、ケースに関するお知らせなどケース管理の自動化に役立つオプションを制御できます。
https://help.salesforce.com/articleView?id=sf.customize_supportrules.htm&type=5
</details>

<strong>３２．(2つ選択)ナレッジ記事はどのように使用できますか？
A.Salesforceの回答を表示するには
B.Salesforceソリューションを表示するには
C.顧客のセルフサービスを表示するには
D.お客様のケースを解決するには

<details><summary>答え</summary>C.顧客のセルフサービスを表示するには
D.お客様のケースを解決するには
解説：
◇
知識ベース（ナレッジ記事）のメリットは下記となります。
・顧客が自分で回答を見つける
・エージェントの生産性の向上（知識ベースでは、組織の知識がリポジトリに格納されます。新しいエージェントには、自己学習できる場所となります。エージェントは質問されるたびに、すべての質問を最初から調べる必要がありません。代わりに、初めての質問があったときにエージェントがその回答を記事に記録します。次に同じ質問をされたときに、他のエージェントでも、知識ベースで記事を検索して用意された回答を提供できます。）
https://trailhead.salesforce.com/ja/content/learn/modules/lightning-knowledge-basics/get-started-with-lightning-knowledge
</details>

<strong>３３．(3つ選択)活動（TODOと行動）ではどのようなカスタマイズが可能ですか？
A.ワークフロールール
B.カスタム項目
C.項目追跡
D.割り当てルール
E.入力規則

<details><summary>答え</summary>A.ワークフロールール
B.カスタム項目
E.入力規則
解説：
◇Aについて
ワークフロールールには、TODOと行動のオブジェクトを選択できるし、TODOと行動の項目も更新できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634241/592d9704-8493-0233-125b-58716223c403.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634241/ea001bf2-c883-1fb4-2850-7df0ea1f322d.png)
◇Bについて
TODOと行動にカスタム項目を追加するには、親となる「活動」にカスタム項目を追加します。この項目を使用可能にするには、[カスタム項目の新規作成] プロセスのステップ 4/4 で、TODOページレイアウト／行動ページレイアウトを選択する必要があります。
<font color=red>※活動は抽象オブジェクトに、TODO・行動はインスタンスオブジェクトに該当すると想像してみてください。</font>
https://help.salesforce.com/articleView?id=000335522&type=1&mode=1
◇Cについて
活動もTODOも行動も、項目履歴を追跡することができません。
https://help.salesforce.com/articleView?id=sf.tracking_field_history.htm&type=5
◇Dについて
割り当てルールにより、リードとケースが指定されたユーザまたはキューに割り当てられます。それ以外のオブジェクトに影響を及ばすことはできません。
https://help.salesforce.com/articleView?id=sf.creating_assignment_rules.htm&type=5
◇Eについて
活動には入力規則が使用できませんが、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634241/270ea13b-6076-9a84-8abf-4ffd53a8baed.png)
TODOと行動には、入力規則が使用できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634241/a5411331-94e9-3007-1d3a-3d85cacdf754.png)
<font color=red>つまり、親で項目を追加し、子で項目を使用・制御し、どちらも項目を追跡できません。</font>
</details>
Node.jsでUNIX domain socketを使ってhttpサーバを立ち上げる方法


# 事前準備

```bash
npm i -g typescript ts-node node-fetch 
```

# コード

```typescript
// サーバーサイドコード
import http from 'http'
import fs from 'fs'

const socketPath = '/tmp/http-test.sock'

// socketファイルがあると立ち上がらないため消しとく
fs.existsSync(socketPath) && fs.unlinkSync(socketPath)

const server = http.createServer((_, res) => {
  res.statusCode = 200
  res.setHeader('Content-Type', 'text/plain')
  res.end('Hello, World!\n')
})

// ちなみにexpressでも、下記listen方法で動きます。
server.listen(socketPath, () => {
  console.log('Example app listening on socket', socketPath)
})
```

---

```bash
# curlで叩く場合
curl -v --unix-socket /tmp/http-test.sock http:/index.html
```

---

```typescript
// クライアントコードの場合
import http from 'http'
import fetch from 'node-fetch'

const socketPath = '/tmp/http-test.sock'

const run = async (): Promise<void> => {
  const agent = new http.Agent({ socketPath }) // 動くのだが、tsのチェックがキツイとエラー扱い。。。
  const res = await fetch('http://localhost/', { agent })
  console.log(res.status)
}
run().catch(console.error)
```

# コメント

上記で動きます。

注意点としては、node-fetchを使ったクライアントコードがtypescriptで厳し目の設定の場合エラーとなることです。

[出典：node-fetch-issues-336](https://github.com/node-fetch/node-fetch/issues/336)

エラーになって実行できない場合、下記でチェックをスキップして、トランスパイルのみで実行出来ます。

```bash
ts-node --transpile-only client.js
```

クライアントコードは、2021/3/10時点で動いてますが、issueではバグとかなんとか言っているので、この書き方はいずれ動かなくなるかもしれません。

まぁ、curl動けば確認できますし、仕様が変更になったらコードを変更すればいいだけですから、そんなでもないですね。
【AWS Cloud9】heroku openが出来ない

#1. エラー内容
- AWSのCloud9で「heroku open」をしたところ、下記エラーが出た。

```CommandLine.
 ▸    Error opening web browser.
 ▸    Error: Exited with code 3
 ▸
 ▸    Manually visit https://（アプリ名）.herokuapp.com/ in your browser.
```

#2. 解決方法
- 「https://（アプリ名）.herokuapp.com/」をクリックしてアクセスするだけ。

#3. エラー原因
- heroku openは、デプロイしたアプリをWEBブラウザで開くコマンドでのこと。WEBブラウザがインストールされていないAWS Cloud9では実行できない。
- コマンドラインに表示されているURLをクリックして直接アクセスしてね、とのこと。

# 参考記事
https://teratail.com/questions/135027
Homebrewを使ってローカルのphpバージョンを設定する場合

ローカルでcomposer updateしたくって、ローカルのphpのバージョン古いよ？って言われて、困ったので調べた内容記して起きますｌ．

## 1. Homebrewに下記をインストール

```
brew install php　# 入れておかないと怒られるので追加しておく
brew install php@7.4
```

## 2. Homebrewにlocalのphpをphp@7.4へ切り替える（※今回は7.4のところは任意のバージョンで）

```
brew unlink php && brew link --overwrite --force php@7.4
```

## 3. 上記のcommandで出てきた案内通りに`~/.zshrc`へechoで指定のパスを追加

## 4.下記コマンドで、`~/.zshrc`の内容を反映

```
source ~/.zshrc
```
FirebaseのコレクションのドキュメントIDを自ら生成し、柔軟性を持たせた

#はじめに
ドキュメントを削除・編集する際に、documentIDを取得しないとできないことに困りました。
なので、firebaseのコレクションの中身のドキュメントを指定し、編集・削除をしたい時に、firestoreがランダムで生成したIDを取得することが困難であったため（力不足が大きいかも。。）、ランダムでIDを生成してくれる`shortid`を使用し、コレクションのドキュメントIDを管理・操作しやすくしました。

###shortidとは、
 64文字（A-Z, a-z, 0-9, _-）をランダムな文字列を定義し、ユニークなIDを生成。詳しくは、[こちらへ(Github)]
(https://www.npmjs.com/package/shortid)。

###実際にコードを書くと、
```javascript
import React, { useState, useEffect } from "react";
import firebase from "../config/Firebase";

import shortid from "shortid";

const sample = () => {
  const initialState = shortid.generate(); // shortid.generate()でランダムID生成
  const db = firebase.firestore().collection("text");

  const [post, setPost] = useState([]);
  const [text, setText] = useState("");
  const [textId, setTextId] = useState(initialState); // ID

  useEffect(() => {
    db.onSnapshot((snapshot) => {
      const posts = snapshot.docs.map((doc) => {
        return doc.data();
      });
      setPost(posts);
    });
  }, []);

  const handleText = (e) => {
    e.preventDefault();
    // 生成したIDをdocへ
    db.doc(`${textId}`).set({
      text: text,
      date: new Date(),
      id: textId,
    });
    setText("");
    setTextId(textId);
  };

  return (
    <>
      <div>
        <h1>TODOリスト</h1>
        <form onSubmit={handleText}>
          <div>
            <p>やることは？</p>
            <input
              id="text"
              type="text"
              value={text}
              onChange={(e) => setText(e.target.value)}
            />
          </div>
          <button>送信</button>
        </form>
        <ui>
          {post.map((list, id) => {
            <li key={id}>
              <p>{list.text}</p>
              <button
                onClick={(e) => {
                  e.preventDefault();
                  db.doc(`${list.id}`).delete();
                }}
              >
                削除
              </button>
            </li>;
          })}
        </ui>
      </div>
    </>
  );
};

export default sample;
```

こうする事でブラウザ上ではなく、Firebaseのコレクションのドキュメント毎のテキストを`delete()`メソッドで削除したいIDを指定し、削除することができます。また、`update()`でテキスト内容を編集し更新することも可能になりました。

##わざわざIDを生成した理由、、
`collection('text').add()`を使用すれば、documentIDは自動生成されるが、`delete()`メソッドなど、documentIDをしてする場合、
`collection('text').doc('ドキュメントID')`.delete()としないと削除できないため、documentIDを取得する手段があまりなかったので、遠回りのような形でやりました。

##感想
shortidを使用することで、ユニークなIDを生成することができ、少ないコードですむことができました。また。documentIDを自ら生成することで、テキスト毎に削除・編集が可能となり、ユーザーが管理しやすいようになりました。
Reactで input file サムネイル表示

#Reactで input file サムネイル表示

`<input type="file" />` で画像のサムネイルを表示したい


## コンポーネントの内容

```tsx:InputFile.tsx

import React, { useState } from 'react'

export interface Props {
  name: string
  defaultValue?: any
}

const InputFile: React.FC<Props> = ({ name, defaultValue }) => {
  const [value, setValue] = useState(defaultValue)
  const onChangeInputFile = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files && e.target.files[0]) {
      const file = e.target.files[0]
      const reader = new FileReader()
      reader.onload = (e: any) => {
        console.log(e.target.result)
        setValue(e.target.result)
      }
      reader.readAsDataURL(file)
    }
  }
  return (
    <div>
      <input type="file" name={name} id="" src={value} onChange={onChangeInputFile} />
      <div>
        <img src={value} />
      </div>
    </div>
  )
}

export default InputFile
```

## codepen

<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="js,result" data-user="HidetoNagamatsu" data-slug-hash="XWNyZYJ" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="React + TypeScript Starter">
  <span>See the Pen <a href="https://codepen.io/HidetoNagamatsu/pen/XWNyZYJ">
  React + TypeScript Starter</a> by Hideto (<a href="https://codepen.io/HidetoNagamatsu">@HidetoNagamatsu</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
カフェでプログラミングしてる風（でも何もやってない）Rust（クソ）コード

# ※本記事はパロディです

本家は以下

https://qiita.com/3S_Laboo/items/660883a0184dabaea65b

# おしゃれなカフェで...

私はエンジニア。
コーヒー片手にLinuxでプログラミングしています。
今日はどんな素晴らしいアート(プログラム)が生まれるかしら？♪

的な事を演出したいときって、誰にだってありますよね？

今日は、カフェでLinuxを使っている時に周りから
「お、この人Linuxで何かやってるな。何か難しそうな事をしているからエンジニアなんだな。素敵だな。」
と思わせる素敵な（Kuso）プログラムを用意しました。

ぜひ、おしゃれカフェで使ってみてください。

# ターミナルで実行すればカッコイイRustコード

```rust:Dungs.rs
use rand::{thread_rng, Rng};
use std::env;
use std::io::{self, Write};
use std::{thread, time};

fn main() -> Result<(), anyhow::Error> {
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        return print_error();
    }
    if args[1] == String::from("states") {
        return dung_states_change();
    } else if args[1] == String::from("compiler") {
        return dung_compiler();
    }
    print_error()
}

fn print_error() -> Result<(), anyhow::Error> {
    println!("[states|compiler]");
    panic!();
}

fn sleep(sleep_time: u64) {
    thread::sleep(time::Duration::from_millis(sleep_time));
}

fn dung_states_change() -> Result<(), anyhow::Error> {
    let mut rng = thread_rng();
    let x = 50; // 50文字まで#を表示する
    loop {
        for _ in 0..=x {
            print!("#");
            io::stdout().flush()?;
            if rng.gen_bool(1.0 / 5.0) {
                sleep(100);
            } else {
                sleep(10);
            }
        }
        println!("done!!");
    }
}

fn dung_compiler() -> Result<(), anyhow::Error> {
    let mut rng = thread_rng();
    let x = 50; // 50個の数値を表示する
    loop {
        for _ in 0..=x {
            let num = rng.gen_range(0..=9);
            print!("{}", num);
        }
        print!("\n");
        io::stdout().flush()?;
        if rng.gen_bool(1.0 / 5.0) {
            sleep(500);
        } else {
            sleep(100);
        }
    }
}
```


これでちょっとイイ感じのスーツ着て、髪の毛をナチュラルにキメて
片手にコーヒー、片手にPinePhoneで、ちょっと考えてるフリすりゃもうデキる人間にしか見えないでしょ？！
写真撮ってsnsにアップすればより多くの満足感が得られますね！

自己肯定感爆上げですわ！！

# ついつい。。。

Qiitaからメールが来た時に笑わせてもらった勢いで作っちゃいました。笑
動画の貼り方も調べず、結局貼っていませんしね。苦笑
これを恥ずかしげもなくカフェに持ち込めればテンションが上がるはず。
自己演出は結構テンションが上がりますね。


初めての投稿がこんなんですみません。

# P.S. 

『たまご家族』のLINEスタンプ可愛いですよ。私も買いました。
みんなも買っていますし、あなたもいかがですか？

https://store.line.me/stickershop/author/1538377
サーバーサイドからOAuth認証が必要なAPIを叩く

Firebase cloud functionからGoogle calender APIを叩こうと思ったらめっちゃ大変だったので、参考文献含め備忘録。

#OAuth認証について
OAuth認証については以下に詳しく書いてある。
https://developers.google.com/identity/protocols/oauth2?csw=1

Qiitaではこちらの記事が非常にわかりやすく、参考になった。
[一番分かりやすい OAuth の説明](https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be#:~:text=%E3%80%8C%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%81%AE%E8%A6%81%E6%B1%82%E6%96%B9%E6%B3%95,%E8%AA%8D%E5%8F%AF%E3%83%95%E3%83%AD%E3%83%BC%E3%81%AE%E8%AA%AC%E6%98%8E%EF%BC%88%E5%8F%82%E8%80%83)

#GoogleのAPIにおけるPythonでの様々な認証方法
以下の記事が非常に詳しい。参考にさせていただいた。
[PythonプログラムでGoogle認証してGoogleのサービスを利用する](https://www.marketechlabo.com/python-google-auth/#%E8%AA%8D%E8%A8%BC%E9%83%A8%E5%88%86%E3%81%AE%E5%AE%9F%E8%A3%85)

#サーバーサイドから認証するには
ここで少し触れている

https://developers.google.com/identity/sign-in/android/offline-access

以下は冒頭の日本語訳
>以前のサインインの追加 手順では、アプリはクライアント側でのみユーザーを認証します。その場合、ユーザーがアプリをアクティブに使用している間のみ、GoogleAPIにアクセスできます。サーバーがユーザーに代わって（おそらくオフラインのときに）Google API呼び出しを行えるようにする場合、サーバーにはアクセストークンが必要です。

また、以下にも方法について記述がある
https://zenn.dev/d_forest/articles/f78078f33a7f2748d5d9

つまり、

**ユーザーはアプリケーションサイドでGoogleアカウントにログインし、auth_codeを生成して、サーバーへ渡す。サーバーは、受け取ったauth_codeからaccess_tokenとrefresh_tokenを生成して、ユーザーの代わりに認証を行う。**

一旦はこれの動作が確認できるように、ローカルで動くように実装した。

#実装
ほぼこの記事の通りで行けた
https://google-auth-oauthlib.readthedocs.io/en/latest/reference/google_auth_oauthlib.flow.html

```python
import datetime
from google_auth_oauthlib.flow import Flow


flow = Flow.from_client_secrets_file(
    'credential.json',
    scopes=['https://www.googleapis.com/auth/calendar.readonly'],
    redirect_uri='urn:ietf:wg:oauth:2.0:oob')

auth_url, _ = flow.authorization_url(prompt='consent')

print('Please go to this URL: {}'.format(auth_url))

code = input('Enter the authorization code: ')
credentials = flow.fetch_token(code=code)

# Call the Calendar API
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
print('Getting the upcoming 10 events')
calendarId='primary'
timeMin=now
maxResults=10
singleEvents=True
orderBy='startTime'
session = flow.authorized_session()
events = session.get(f'https://www.googleapis.com/calendar/v3/calendars/primary/events?calendarId={calendarId}&timeMin={timeMin}&maxResults={maxResults}&singleEvents={singleEvents}&orderBy={orderBy}').json()

if not events:
    print('No upcoming events found.')
for event in events:
    start = event['start'].get('dateTime', event['start'].get('date'))
    print(start, event['summary'])

```

上記のコードがそのままローカルで動くので、cloud functionに実装する際は、上記ではinputで受け取っている変数codeを、APIの引数として受け取るように実装すれば良い。
組織アカウントにおけるPower Automate Desktopの試用ライセンスについて(随時更新予定）

皆さんこんにちは、マイクロソフトの川端です。
Power Automate Desktopの内部挙動（ライセンス）について確認できている内容をご紹介していきます。

組織アカウントでライセンスが付与されていないユーザーからAzure ADでPower Automate Desktopのログインを行いますと、
以下のような「Microsoft Dataverseデータベースが見つかりませんでした」（組織アカウントでDataverseが保持されていない場合）や「プレミアムを利用する」というボタンが見える状況となります。
組織内でユーザー自身から利用を開始するためには「プレミアムを利用する」から利用を開始できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/808554/5782d50e-e0f8-41ef-0604-34de23d62474.png)

右上にある「プレミアムを利用する」をクリックすると、「試用の開始」と「今すぐ購入」が指定できるようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/808554/d02ebb59-5527-4fc8-d2ed-36480de808db.png)

「試用の開始」を選択しますと、試用でライセンスが使える状態となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/808554/23c0cada-c907-9e5e-3e95-07952189b478.png)
試用中は、このユーザーに対して他のライセンス付与が何も無い場合には「既定の環境(default)」のみが選択できる状態となります。ただし、実際にデスクトップフローを利用するためには、既定の環境にDataverseの容量が必要である点に注意します。

Microsoft365管理センターではこのユーザーにはどのようなライセンスが付与されているのでしょうか？
https://admin.microsoft.com/
を環境管理者として参照してみます。

そうすると「Microsoft Power Automate Free」というライセンスがテナント下で当該ユーザーに対して新しく付与されていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/808554/24cb6632-f193-5bcc-797d-f49029fd6712.png)

こちらのPower Automate Freeライセンスですが、現状(2021/03/10)追跡目的のため、こちらのライセンスが直接ライセンス付与はく奪の制御とならない点に注意です。
https://docs.microsoft.com/ja-jp/power-automate/organization-q-and-a#what-is-the-power-automate-free-license

<del>現状試用版の期間は90日間です。</del>
試用期間は30日（延長可能30日）の合計60日です（2021年4月時点）。
https://docs.microsoft.com/ja-jp/power-platform/admin/powerapps-flow-licensing-faq#how-long-is-the-free-trial-period
IFTTTがSpotifyのRecently Played取得に対応した！

# Spotify IFTTT トリガー 増えた

タイトルどおり、IFTTTのトリガーにSpotifyでの音楽再生が追加されました。

(今までは何を取得できてたのか分かりませんが、たぶんプレイリスト絡みしかトリガーになってなかった)

これの何が画期的なのかというと、SpotifyのAPIは「最近の再生」を５０トラック(確か)しか保持していなかったのです。

５０トラック以上（というか全部）取得したい！　という欲求はけっこうありました。

# 今まで　頑張る　してた

今まで自分が取っていた手段は、FirebaseのFunctionsで定期的にAPIを叩くスクリプトを書くというものでした。

![スクリーンショット 2021-03-10 12.53.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92027/f0275b33-f2ae-d441-fa77-8c5897e120ea.png)


返ってきた５０トラックはCloud Firestoreに保存します。

問題はもうすでに保存した再生と、まだ保存してないトラック（新規の再生）の区別ができないことで、

（日付を比較などすれば解決するかもですが）

結局保存の際、キーを曲IDにすることで上書き保存していました。

このため、同じ曲を期間を開けて２回再生した場合、新しいほうの再生情報しか残っていませんでした。

# 楽　これから

しかしIFTTTが入ってきたことで、そのへんを任せることができます。

Google Spreadsheetなどに保存できるので、あとは煮るなり焼くなりできます。

![スクリーンショット 2021-03-10 12.54.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92027/ab0b11fd-ac39-1639-d32d-8094f354dd6e.png)


注意点としては本当に「再生された情報を追記」しかしないため、デフォルトでは見出し行が存在しないことです。

（普通に行を上に追加->テキストを入れることで見出しも実現できます）

自分の場合このSpreadsheet保存と、Slackに投稿の２つのIFTTTアプリを作りました。

![スクリーンショット 2021-03-10 12.55.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92027/8f3b1fef-93f8-8237-f238-151eb6e0ad43.png)


欠点として、Freeプランではトリガーの検知が遅い（１時間ごと？）というものがあります。

まあそんなに急ぐアプリではないのですが、テストする際にちょっと大変かもです。

IFTTTアプリ内にあるCheckボタンをクリックすることで手動でチェックできます。
[memo]投稿時刻を日本時刻にする設定

##投稿時刻の表示を変更
投稿時刻の表示を日本時刻に変更し、その後メソッドを用いて時刻を表示します。

###1. ファイル設定の変更
時刻の設定には、Railsのapplication.rbという設定ファイルを扱います。

``application.rbファイル``
そのRailsアプリケーションの開発環境すべてにおいて共通となる設定を記述します。
具体的には次の２行を追記します。

>config.i18n.default_locale = :ja
 config.time_zone = 'Tokyo'


```ruby:config/application.rb
module ChatApp
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.0 
    config.i18n.default_locale = :ja　#追記
    config.time_zone = 'Tokyo'　#追記
   # 中略
```


###2. ja.ymlファイルを作成して、表示する時刻のフォーマットを設定

config/localesディレクトリに「ja.yml」というファイルを作成し次の記述をする

```ruby:config/locales/ja.yml
ja:
  time:
    formats:
      default: "%Y/%m/%d %H:%M:%S"
```


###3. 出力
出力には``lメソッド``を使用します。
l（エル）メソッドは、日付や時刻を表示するRailsのメソッドです。
国や地域によって時刻は異なりますが、lメソッドを使えば指定した現地時間に対応できます。

```ruby:html.erb
<!-- 投稿した時刻を出力する -->
      <%= l message.created_at %>
```
Ruby超初級編　JavaScriptやってた人とっつきやすいと思います。

ProgateでRubyを勉強し始めたのですが、JavaScriptと似ている部分があり覚えやすかったです。
復習のためJSとの比較交えながらRubyについてまとめておきます。
JavaScriptやってる初心者でバックエンド言語何にしようかと迷っている方にはとっつきやすいと思いました。
初心者の言語選定などの参考になれば幸いです。

##コンソールに値を表示させるコマンド　
JSで言うconsole.log。これはJSと少し違う。

```ruby
puts "Hello world"
# メモ書き
```

##変数宣言    
constとかいらないのね

```ruby
name = "げん"
```

##変数の命名　2語以上の場合はアンダーバーで区切る

```ruby
user_name 
```

##変数の上書きができる。
JSのvarみたいな感じと解釈。
JSのconstでは上書きできなかった

```ruby
age = 2
age = 3 #上書きされる
```

##変数の更新
変数xの値を既存の変数xを使って更新

```ruby
x = x + 10  

##省略記法
x += 10
```

##文字列の間に変数を埋め込む。 
JSでいうテンプレートリテラル(``)。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Template_literals
Rubyでは"(ダブルコーテーション)で囲まれた文字列の中に、#{}を含め、その中に変数を書き込むと文字列の間に変数の値を入れることができる。

Rubyで文字列を書く場合は基本的に"(ダブルコーテーション)を使うことにしておこう。

```ruby
name = "げん"
puts "僕の名前は#{name}です"　　
#出力は　僕の名前はげんです　になる
```

##if文　
ifとendで囲む。
JSと比べるとendで終わるのが違う点。
あと、２つ目の条件分岐がelseifではなくelsif。elseの「e」が不要。

```ruby
   score = 94
   if score > 80
      puts "よくできました"
　　elsif　score > 60
      puts "まずまずです"　
   else
      puts "がんばりましょう"    
　　end
```

##条件の組み合わせ
これは一緒。

```ruby
   &&  
```
```ruby
   ||
```

## 配列　


```ruby
   language = ["日本語","英語","スペイン語"]
```
## 配列へのアクセス 
JSと一緒。

```ruby

language[0]
```

## 繰り返し処理　each
これはJSとはだいぶ違う書き方。

```ruby
sports = ["バスケ","サッカー","野球"]
num = 3

sports.each do | sport |
  puts "#{sport}ができます"
end
```
each文内の変数sportはeach文の内側でしか使用できない。
each文の外で宣言された変数はeach文の中身で使用できる。

## ハッシュ　JSで言うオブジェクトのようなもの
```ruby

  {"name" => "gen","age" => 2}
```

## ハッシュへのアクセス　
JSのオブジェクトの角括弧記法と同じ.

```ruby

 pet = {"name" => "gen","age" => 2}
 pet["name"]  # genにアクセスできる
```

##　シンボル　
キーの先頭に「:」をつけると、ダブルコーテーションと同じ役割を果たす。
値を取り出すときも、シンボルを記載する必要がある。

```ruby
 pet = {:name => "gen",:age => 2}
 pet[:name]  # アクセスするときもシンボルを記載
   
```
## シンボル省略形　
けっきょくJSのオブジェクトと一緒やないかい

```ruby
 pet = {name: "gen",age: 2}
 pet[:name]  # アクセスするときは先頭にシンボルの記載はやっぱり必要

```

## ハッシュ内部のキーの有無で条件分岐
JSでも似たような感覚でif文かいてた。

```ruby
 pet = {name: "gen",kind: "柴犬"}
 puts pet[:age]  #nilが出力される。これを利用して条件分岐できる。

 if pet[:age]     #nilが返るのでelse以下の文章が出力される。
   puts "ペットの年齢は#{age}です"
 else 
   puts "ペットの年齢がわかりません"
 end
```
## 配列の中にハッシュが入ることもある。
JSと同様。

```ruby

   arr = [{},{}]
```

# おわり
[UE4] DisregardGCの使い方について



#1. 導入
　UE4のオブジェクトサイクルは、必要な時にオブジェクトを作成し、必要が無くなったタイミングでガーベージコレクションシステムによって削除されます。ガーベージコレクションシステムは自動的にオブジェクト管理をする上で便利ですが、時にはオブジェクトを意図的に削除したくないようなケースもあります。そこで、本記事ではオブジェクトを常駐するための方法について示します。

　オブジェクトを常駐させることは、ロード時間の短縮やロードによるヒッチの回避に役立ちます。必要なリソースを先にロードしたり、再利用することによって、都度必要なロードの機会を削減します。オブジェクトを常駐するには”DisregardForGC”を使用します。

#2. 詳細
　“DisregardForGC”は、指定したobjectをgarbage collectの対象から除外することによってオブジェクトを永続的に生かす仕組みです。以下のリンク先にあるスライドで詳細に説明しています。Resources のページの [UE4におけるLoadingとGCのProfilingと最適化手法]からスライドをダウンロードすることもできます。
https://www.unrealengine.com/ja/resources?lang=ja

以下のスライドと同じです。
https://www.slideshare.net/EpicGamesJapan/ue4loadinggcprofiling

　UE4のオブジェクトは、全オブジェクトを格納する配列(GUObjectArray)で一元管理されています。DisregardForGCは、この配列に格納するオブジェクトの一部を非GC領域に格納することによってGCの対象から除外します。

![2021-03-10_12h43_40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364110/d4871984-ec0c-4f67-e3e2-c5af26b97e7d.png)

　DisregardForGCの対象となるオブジェクトは、エンジンの起動開始直後から`FUObjectArray::CloseDisregardForGC()`が呼ばれるまでの期間にロードされたオブジェクトです。このタイミングは、エンジンの初期化フェーズである`FEngineLoop::PreInit`で、エンジンが必要な全てのオブジェクトのロードが行われますが、プロジェクトで使用するオブジェクトもこのタイミングでロードすることによって、常駐オブジェクトの対象とすることが可能です。

#3. 前提
DisregardForGCを有効にします。 (gc.MaxObjectsNotConsideredByGCを>0の値に設定).

#4. 使用例
 実際にDisregardForGCの対象とすることが可能なロードの定義方法についていくつか紹介します。

###例1
以下はシューターゲームにおける例です。`DefaultGame.ini`で指定したファイルを`PostInitProperties()`のタイミングでロードすることも有効です。
以下の場合、T_ImageAのTexture2Dは永続オブジェクトとして破棄されることはありません。

```cpp:ShooterGameInstance.h
virtual void PostInitProperties() override;
 
UPROPERTY(config)
TArray<FString> StartupPackages;
```

```cpp:ShooterGameInstance.cpp
void UShooterGameInstance::PostInitProperties()
{
	Super::PostInitProperties();
	for (FString PackageName : StartupPackages)
	{
		LoadPackage(nullptr, *PackageName, 0);
	}
}
```

```cpp:DefaultGame.ini:
[/Script/ShooterGame.ShooterGameInstance]
+StartupPackages=/Game/Textures/T_ImageA
```

###例2
別の例として、GameInstanceのコンストラクタでテクスチャをロードしていますが、これが呼ばれるタイミングはDisregardForGCがまだクローズしていないため、ここでロードされたTexture2D オブジェクトは最終的に永続オブジェクトとして破棄されません。​

```cpp:ShooterGameInstance.cpp

UShooterGameInstance::UShooterGameInstance(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	, OnlineMode(EOnlineMode::Online) // Default to online
	, bIsLicensed(true) // Default to licensed (should have been checked by OS on boot)
{
	UTexture2D* DisregardGCTexture = LoadObject<UTexture2D>(NULL, TEXT("/Game/Textures/T_ImageB.T_ImageB"), nullptr, LOAD_None, nullptr);
}
```

###例3
さらに別の例として、UObjectから派生したクラスで`PostCDOContruct`でアセットをロードする例です。
 
```cpp:MyObject.cpp
void UMyObject::PostCDOContruct()
{
	UTexture2D* DisregardGCTexture = LoadObject<UTexture2D>(NULL, TEXT("/Game/Image/TextureC.TextureC"), nullptr, LOAD_None, nullptr);
}
```

#5. QA
Q:どのオブジェクトでロードしたものが対象となる？
A:UObjectを継承したクラスが対象。非UObjectのクラスは対象外。

Q:どのモジュール(タイミング)でロードしたものが対象となる？
A:エンジンの初期化時に全UObjectのCDO(CreateDefaultObject)処理が走るため、そこに関連する`Constructor`、`PostInitProperties`, `PostCDOContruct`でロードしておけばDisregardForGCの対象となります。使用例を参照。
LPIC 勉強記録 第2章 正規表現とviエディタ

#第2章 正規表現とviエディタ

| 記号 | 説明 | 
|:----------|:----------|
| c      |文字cに一致(cはメタキャラクタではないこと)    |
| \c    | 文字cに一致(cはメタキャラクタであること)     | 
| .    | 任意の文字に一致   | 
| ^    | 行の先頭   | 
| $    | 行の末尾   | 
| *    | 直前の文字が0回以上の繰り返しに一致   | 
| ?    | 直前の文字が0回もしくは1回の繰り返しに一致   | 
| +    |直前の文字が1回以上の繰り返しに一致    | 
| []    |  []内の文字グループと一致  | 

| 主なオプション | 説明 |
|:-----------|:----------|
|[abAB]|a、b、A、Bのいずれかの文字|
|[^abAB]|a、b、A、B以外のいずれかの文字|
|[a-dA-D]|a、b、c、d、A、B、C、Dのいずれかの文字|

`grepコマンド(egrepコマンド)`で、条件に一致しない行を検索するには、`-vオプション`を使用する

`pgrepコマンド`は、現在実行中のプロセスを調べて、ユーザ名、UID、GIDなどを基に、プロセスIDを検索する

| 主なオプション | 説明 | 
|:----------|:----------|
| -u euid     |指定された実効ユーザIDに該当するプロセスIDを検索   |
| -U uid    | 指定された実ユーザIDに該当するプロセスIDを検索  | 

| ユーザIDの種類 | 説明 |
|:-----------|:----------|
|実ユーザID|プログラムを起動したユーザ(プロセスの所有者)|
|実効ユーザID|プログラムが動作するときの権限|

`fgrepコマンド`は、引数で指定された検索文字列を基に検索する
正規表現は使用しない

| 主なviコマンド | 説明 | 
|:----------|:----------|
| h または ←     |カーソルを左に1文字移動   |
| j または ↓    | カーソルを下に1文字移動  | 
| k または ↑   |カーソルを上に1文字移動  | 
|l または →   | カーソルを右に1文字移動 | 
| 0    | 現在の行の先頭へ移動| 
| $    | 現在の行の末尾へ移動 | 
| G    | 最終行へ移動  | 
| 1G    |1行目へ移動  | 
| nG    |  n行目へ移動|
| [Ctrl]+[b]   | ファイルの先頭に向かって1画面ずつスクロール |
| [Ctrl]+[f]    |ファイルの末尾に向かって1画面ずつスクロール | 

| 主なviコマンド | 説明 | 
|:----------|:----------|
|:w|ファイル名を変更せずにそのまま保存|
|:w!|ファイル名を変更せずに強制的に保存|
|:q|ファイルを保存せずに終了|
|:q!|ファイルの内容の変更があっても、保存せずに強制的に終了|
|ZZ or :wq|保存と終了を同時に行う|
|:wq!|強制的に保存と終了を行う|
|:! ls -l 編集しているファイル名|viから抜けずにコマンドを実行する|

ファイルにどのようなパーミッションが与えられているかを確認するには、`lsコマンド`に`-lオプション`を使用する
vi実行中なら、コマンドラインモードから`:!`を使用する

| 主なviコマンド | 説明 | 
|:----------|:----------|
|i     |カーソルの前に文字を挿入   |
| a  | カーソルの後に文字を挿入  | 
|  o |カーソル行の下に新しい行を作成し、その先頭から挿入が開始 | 
|I | カーソル行の先頭に文字を挿入| 
| A   |カーソル行の末尾に文字を挿入 | 
|  O  |カーソル行の上に新しい行を作成し、その先頭から挿入が開始  | 

| 主なviコマンド | 説明 | 
|:----------|:----------|
|x    |カーソル上の1文字を削除  |
|dw  | カーソルから次の単語を1単語削除  | 
|dd |カーソル行を削除 | 
|nx |カーソルから右にn文字削除 | 
|D    |カーソルから行の最後まで削除 |
|ndd  | カーソル行からn行削除 | 
|dG| カーソル行から最終行まで削除 | 
|dH  | 画面の1行目からカーソル行まで削除 |

| 主なviコマンド | 説明 | 
|:----------|:----------|
|yy|カーソル行をコピー |
|yw|単語のコピー | 
|P|カーソル行の上の行に貼り付け| 
|p| カーソル行の下の行に貼り付け|

| 主なviコマンド | 説明 | 
|:----------|:----------|
|/文字列|現在のカーソル位置からファイルの末尾に向かって検索|
|?文字列|現在のカーソル位置からファイルの先頭に向かって検索|
|n|次を検索|
|N|前を検索|

オプション機能の設定には、`:setコマンド`を使用する
vi起動時に常に同じ設定となるようにするには、ホームディレクトリ以下にある`.exrcファイル`を作成し、設定情報を記述する

| テキストエディタの切り替え | コマンド | 
|:----------|:----------|
|viモードに設定する場合|set -o vi|
|emacsモードに設定する場合|set -o emacs|

#終わりに
3周目終了！
この辺りはポンポン進めるから、ノーストレス！
【まとめ】Vueでのデータの取り方

Vueでのデータの取り方をまとめる。

1. immutable properties
2. mutable properties
3. computed properties

の3つに分けて考えるとわかりやすい。

## immutable properties

不変の値。props。

templetes以上では、vue-router経由でidとかだけ受け取れる。
orgnism以下では、親から引き渡される形で使う。

できる限りこちらからデータを取り切るのがポイント。
特にoranism以下では。

## mutable properties

変化する値。data。リロードすると中身変わったりする。

templates以上では、サーバーへAPI叩くことで、レスポンスをdataへ流し込む。
時間がかかるのが欠点。

どこでもできるのがstoreを使うパターン。
storeは、どこからでもアクセスできる変数のjsファイル。
プロパティとそれをセットするためのメソッドからなる。
欠点は、どこで初期化すれば良いかわからん点と、リロードすると消える点。
なので、基本的にtemplates以上で、かつlogin情報など消さなくて良いものでしか使いたくない。

storeはアンチパターン。これが言いたかった。

## computed properties

これがVue独特の概念で最初苦労した。
computed、つまり、propsやdataもとに、演算された真偽値を置くところ、で覚えるのが良い。

## まとめ
コンポーネントごとにデータの取り方を分けて考えると良いと思う。
- templates以上
vue-router経由のpropsと、サーバーからのdata
- organism以下
親からのprops

これでFA。storeはしらない。
Arduino-ESP32のBLEをNimBLEに置き換えてみたらちょっぴり違った(クライアント側）

#概要
Arduino-ESP32 1.0.1 の頃に書いたBLEライブラリを使ったクライアント（BLEセントラル）のコードを、 1.0.5 + NimBLE に書き換えてみた（2021/3時点)
基本的には `#include` するヘッダを `<BLE*.h>` をやめて `<NimBLEDevice.h>` 1つだけにしてから、BLE* ってクラスを NimBLE* ってクラスにすれば9割5分完成、なんだけど、ちょいちょい仕様が違ったので、自分のコードで違ったところ4箇所まとめ。
(全ての機能を使っているわけでも、全ての違いを網羅したわけでもありません）

#見つかった違い
###1. NimBLEAdvertisedDeviceCallbacks::onResult の引数の型
`BLEAdvertisedDeviceCallbacks::onResult` のパラメーターの `BLEAdvertisedDevice` はオブジェクト渡しで、
`NimBLEAdvertisedDeviceCallbacks::onResult` のパラメーターの `NimBLEAdvertisedDevice*` はポインタ渡し。Nim付けてコンパイルするとエラーで教えてくれる。

NimBLE_Client.ino サンプルのように `MyNimBLEAdvertisedDeviceCallbacks` 派生させてるなら、その中の `onResult` のパラメータ型変えて、 `onResult` 内の `.` を `->` にしまくり、`&`でアドレス渡してたら`&`とらずポインタそのままにする感じで。

###2. NimBLEAddress は PUBLIC, RANDOM のような type 情報も持つ
自分は古いコードで

```cpp
static BLEAddress bleaddrServer[NUMBER_OF_TARGET_DEVICE] = {
 std::string("XX:XX:XX:XX:XX:XX"),
 std::string("YY:YY:YY:YY:YY:YY"),
 std::string("ZZ:ZZ:ZZ:ZZ:ZZ:ZZ")
};
```

的にやってて `BLEClient::connect` 時にこれ使って、`BLE_ADDR_TYPE_RANDOM` 指定していたので、配列を単純に `NimBLEAddress` に置き換えたら、コンストラクタが第二引数を type をデフォルト値で補うことになる。デフォルト値は

    NimBLEAddress(uint8_t address[6], uint8_t type = BLE_ADDR_PUBLIC);

なので、`BLE_ADDR_PUBLIC`にされちゃいます。RANDOMにしたかったので普通にコケた。(細かいが定数名に _TYPEが 入るか入らないかの違いもある）
適切に初期化するには、複数引数をコンストラクタに渡して配列を初期化する必要がある。

```cpp
static NimBLEAddress bleaddrServer[NUMBER_OF_TARGET_DEVICE] = {
 NimBLEAddress(std::string("XX:XX:XX:XX:XX:XX"), BLE_ADDR_RANDOM),
 NimBLEAddress(std::string("YY:YY:YY:YY:YY:YY"), BLE_ADDR_RANDOM), 
 NimBLEAddress(std::string("ZZ:ZZ:ZZ:ZZ:ZZ:ZZ"), BLE_ADDR_RANDOM)
};
```

（まぁ違う方法で解決しちゃったのだけど。3参照）


###3.BLEClient::connect
`BLEClient::connect` のとき、引数に `std::string` なアドレスと `BLE_ADDR_TYPE_RANDOM` 等を指定できるオーバーロード関数があったが、NimBLEにはない。
2で書いたとおり、NimBLEAddress型で `NimBLEAddress( std::string なアドレス, BLE_ADDR_RANDOM )` と準備してから `connect` に渡せば良い。(定数名の _TYPE 有無に注意)

なお、スキャンして見つかったら接続、というコードであれば、スキャンで見つかった情報に RANDOM/PUBLIC とかが入ってるので、スキャンして見つけた `NimBLEAdvertisedDevice` の `getAddress()` で得られる `NimBLEAddress`型のアドレスを、グローバル変数にでもコピーしておいて（実体7バイトだからええやろ) `connect` に渡せば自分で PUBLICだなんだ考えなくても良い。

サンプルコード NimBLE_Client.ino では、「`NimBLEAdvertisedDevice` のポインタ」をグローバル変数に入れてたが、ポインタの先の本体がいつどうなるか知らない自分には怖いし、`NimBLEClient::connect` のパラメーターに `NimBLEAdvertisedDevice*` 渡す場合の関数見たけど中で `device->getAddress()` してるだけだったので、だったら `NimBLEAddress` オブジェクトのコピーを7バイト、グローバル変数においとけばいいかな、って感じ。

###4. 16bit UUID
CCCD の UUID は `NimBLEUUID(uint16_t(0x2902))`だとうまくいったが、`NimBLEUUID  uuidCCCD( "00002902-0000-1000-8000-00805f9b34fb" )` って128bit表記から生成したらうまくいかなかった。

NimBLEUUID は内部で `ble_uuid_any_t m_uuid` ってメンバ変数を持っていて、コンストラクタも

```cpp
NimBLEUUID::NimBLEUUID(uint16_t uuid) {
    m_uuid.u.type        = BLE_UUID_TYPE_16;
    m_uuid.u16.value     = uuid;
    m_valueSet           = true;
} // NimBLEUUID
```

なーんてやってるから、128bit と 16bit で同じにならないみたい。（同じだと思っていたのに）
なにやら 32bitってのもある模様。

#おわりに
こんだけで、`NimBLERemoteService` とか `NimBLERemoteCharacteristic`, `NimBLERemoteDescriptor` はそのまんまで動いた。client や notify の callback も。
(全ての機能を使っているわけでも、全ての違いを網羅したわけでもありません）

サーバー（ペリフェラル）側も UUIDとAddress は共通として、その他の差はそんなにないんじゃないかな。ペリフェラルのコード自分で書いたことないから知らんけど。

bin は NoOTA にしなくても普通に収まるくらい小さい(3割減くらい)のでありがたいです。
【AWS】AWS初心者がRoute53＋SESを使って、独自ドメインでGmailからメールを送信してみた

つい2，3週間ほど前からAWSに触れているのですが、AWSにはメール送信する機能があるなんてめちゃめちゃ便利だなと思いました。

他にもSSLの証明書を無料で発行できるしハンパない！

今日はそんなこんなでSESを使って独自ドメインでGmailからメールを送信する方法を書いていきます。

#はじめに#

・Route53に既にドメインを登録している前提で話していきます。

・SESで独自ドメインでメールを送信するためには、メールの受信を「Rule Sets」で行えるようにしてください。

もし行っていない場合は、僕が書いた記事で良ければどうぞ。

[【AWS】AWS初心者がRoute53＋SES＋S3＋Lambdaを使って、独自ドメインに送られてきたメールをGmailに転送してみた](https://qiita.com/akki-memo/items/71cab2a38a0c5a6d904f)

また、SESでのメール受信は東京リージョンではできないのですが、メールの送信は東京リージョンで出来るのでお好きなリージョンを選んでください。説明では東京リージョンを使用していきます。

それでは早速説明していきます！！

#ドメイン認証#

1. サービス検索欄から「SES」と検索
2. 左のメニュー欄から「Domains」を選択
3. 「Verifi a New Domain」をクリック

Route53に登録しているドメイン名を入力

「Generate DKIM Setteings」に**必ずチェック**してください。

すると、色々と認証情報が出てくると思います。

少し下に行き、「Use Route53」をクリック

全ての項目にチェック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/70fe857d-372e-37f0-d1a9-73388e7c9506.png)

「DKIM Status」が「Verified」になればOK！

verifiedにならない場合はドメイン情報が間違っているのでもう一度やり直してください。

#メールアドレス認証#

次にメール認証を行います。

左のメニュー欄から「Email Address」をクリック

「Verify a New Email Address」をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/d0c8f381-4c96-38c7-d9f3-8f238e25011c.png)


お使いのアドレスを入力

すると、S3にメールが届くので、届いたメールをダウンロードしメール内容に``https://~``
のアドレスがあるので、それをコピペしてアクセス

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/33c7fdf8-3703-0c15-07e1-c2d9e7705174.png)

認証が成功すればOKです！

#SMTP設定#

次に、メールを送信するにはSMTPの設定を行わなければいけないのでその設定を行います。

左のメニュー欄から「SMTP Settings」→「作成」

すると、ユーザー名、パスワードが表示されます。

この画面は絶対に閉じないでください！！！

閉じずに次のステップへと進んでください。

#Gmailの設定#

次に、先ほど指定したメールアドレスをGmailで送信できるようにGmailでの設定を行います。

Gmailにアクセス

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/a36ad5d0-6c1f-3b75-82c9-3e8526d34306.png)

「Create My SMTP Credentials」

右上の歯車アイコンをクリックし、「すべての設定を表示」

「アカウントとインポート」から「名前」→「他のメールアドレスを追加」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/732c88c0-e03f-d62b-ce03-b2f81fc2370c.png)

ここは適当に

SMTPサーバーは以下のように設定

・email-smtp.ap-northeast-1.amazonaws.com

東京リージョン以外の人は[https://docs.aws.amazon.com/general/latest/gr/ses.html](https://docs.aws.amazon.com/general/latest/gr/ses.html)を参照

ユーザー名、パスワードは先ほど閉じないでくださいと言った画面のやつを入力

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/5f2cefc2-909f-3a4f-9709-4bc756c9f93d.png)

アカウントを追加

すると、設定したメールアドレス（S3）にメールが届いてあるはずです。

それをダウンロードしてください。

メール内容を見れる方はそのままリンクをクリックするか、コードを入力してください。

ただ、見れない方はbase64でエンコードされた文字列が記載されていると思います。

このコードをコピーして[こちら](https://www.en-pc.jp/tech/base64.php)でデコードしてリンクをクリックするか、コードを入力してください。

認証が成功すればOKです！

試しにGmailから登録したドメインでメールを送信して見て下さい！

いかがだったでしょうか？

SESで中々時間が取られたので、皆さんも頑張ってください！！

以上、「【AWS】AWS初心者がRoute53＋SESを使って、独自ドメインでGmailからメールを送信してみた」でした！

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい！！

あと、最近「ココナラ」で環境構築のお手伝いをするサービスを始めました。

気になる方はぜひ一度ご相談ください！

Thank you for reading
browsermob-proxy-py 修正済みだけど修正できないバグの話

# 修正済みの修正できないバグ
selenium + browsermob-proxyの色々検証を継続しているのですが。
その中で、（おそらく）サンプルどおりのコードを書くと、終了時がうまく行かない箇所があります。

原因は大して難しくないし、対処も難しくはないのですが、運用性を考えると色々と難しいという話です。

# brosermob-proxy-py(v0.8.0)の既知の問題

## 問題の詳細
サンプルコードとしては以下が紹介されています。

https://github.com/AutomatedTester/browsermob-proxy-py

```py
from browsermobproxy import Server
server = Server("path/to/browsermob-proxy")
server.start()
proxy = server.create_proxy()

from selenium import webdriver
profile  = webdriver.FirefoxProfile()
profile.set_proxy(proxy.selenium_proxy())
driver = webdriver.Firefox(firefox_profile=profile)


proxy.new_har("google")
driver.get("http://www.google.co.uk")
proxy.har # returns a HAR JSON blob

server.stop()
driver.quit()
```

上記のServer("path/to/browsermod-proxy"）の部分で、bolowsermob-proxyのコードと一緒に配布されている、プロキシプロセスを呼び出すラッパのスクリプト（browsermob-proxyをzipでおとしてきて解凍すると、bin/browsermob-proxyという名前で入ってるやつ）を起動します。

で、最後にserver.stop()でプロキシプロセスを停止するはずなのですが、、、止まらないんですね。。
コレが今回の問題。

## 問題の原因
原因は簡単で、server.stop()ではラッパスクリプトだけ停止して、そいつが呼び出したプロキシプロセスを停止し忘れているから。
これが、browsermob-proxy-pyのv0.8.0（現時点の最新配布版（だと思う））の仕様。

## 問題の対処
２つ思いつきます。
１つ目は、browsermob-proxy-py側を改修。子プロセスまで殺す（プロセスグループ単位で殺す）
２つ目は、呼び出されるラッパスクリプト側に手を入れて自分が死ぬときに子供のプロキシプロセスもちゃんと殺すようにする。

## 対処の実施
対処方法は簡単なのですが、、運用性を考えるとすごくめんどくさい。

まず１つ目のbrowsermob-proxy-pyの改修ですが、実はgitを見に行くと、もうすでにそういうコードに修正されています。
ただ、pypiで配布しているのはその修正する前のv0.8.0までなんですよ。。
手元のコードがおいてある領域（~/.local/lib/python...）に対してgitから最新コード持ってきてもいいし、最悪手で修正してもいいですが、そうすると今後pipで管理ができなくなる。（正しくは、pipでアップデートしたら修正が吹っ飛ぶので、それをしない/pip前に１回確認する運用を組まないといけなくなり、致命的に煩雑になる）。

２つ目ですが、これも手で直しちゃえばいいのですけれど、これも上記と同様、公式さんから次バージョンが配布されたときに、同じ修正をかけないといけなくなる。

どちらも、「公式によるバージョン管理」から逸脱しないといけなくなってしまうので、これは運用上致命的にめんどくさい。

## 別のアプローチ
公式さんの管理外で実施できる方法をとれば、上記のジレンマからは解消されます。
動作未確認ですが、"path/to/brosermob-proxy"の部分で、ラッパスクリプトが最終的に呼び出しているプロキシプロセス（java）を直接呼び出すようにするか、、
brosermob-proxy-py自体に手を加えると公式さんの管理から外れますが、それを使うコードは自分管理の配下なので、上記のジレンマは怒らないわけです。
ただ、コレでほんとにちゃんと動くかは不明。


# まとめ
そんなに長期間使う予定があるものでもないので、プロキシプロセス呼び出すラッパスクリプトに手を加える方法で対処しようと思います。

ただ、まさにコレがひとの作ったコードを使う時の課題。
ひとが作ったコードの修正管理やバージョン管理は、完全に作成者に任すのが最も効率的で最適な選択肢だと思いますが、それが滞るとこうなる。。

browsermob-proxy-pyはpullリクエストやイシューの投稿などは続いているようですが、コード自体は3年前で更新止まってます。
まぁbrowsermob-proxy自体の方も似たような状態なのですけどね。
それを言うならSeleniumもだろって？　、、、ええ、まぁ、、、
枯れていると言うと聞こえはいいですが、古い知識に頼り切りになって視野が狭まってるだけなきもするので、きっちり勉強線とな。。
macOS Mojave 10.14 で YouCompleteMe のインストール時に起きる問題の解消方法

## 概要

[vimって極めればvscode並のIDEになるんじゃないの？ - @ulwlu](https://qiita.com/ulwlu/items/98901f4c4f0683e7aa57)を参考にVimをカスタマイズしていたところ [YouCompleteMe](https://github.com/ycm-core/YouCompleteMe) でいくつか問題があったためまとめます。

## YouCompleteMe インストール

現在のvimrcはこれ→ [dotfiles/dotfiles/.vimrc
](https://github.com/ulwlu/dotfiles/blob/master/dotfiles/.vimrc)
にはYouCompleteMeが記載されていなかったため個別で入れてみました。

「macos YouCompleteMe install」で検索すると [ChengLong/InstructionsForYCM-Mac.md](https://gist.github.com/ChengLong/6208775) を見つけて次のように実行しました。

```shell
$ brew install cmake
$ cd ~/.vim/bundle
$ git clone https://github.com/Valloric/YouCompleteMe.git
$ mkdir YouCompleteMe/ycmbuild
$ cd YouCompleteMe
$ git submodule update --init --recursive
$ cd ycmbuild
$ cmake -G "Unix Makefiles" . ~/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/
$ make ycm_core
```

## fatal error: 'filesystem' file not found

最後の `make ycm_core` で次のようなエラーが出ました。

```shell
$ make ycm_core
Scanning dependencies of target ycm_core
[  6%] Building CXX object ycm/CMakeFiles/ycm_core.dir/Candidate.cpp.o
[ 12%] Building CXX object ycm/CMakeFiles/ycm_core.dir/CandidateRepository.cpp.o
In file included from /Users/gremito/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/ycm/CandidateRepository.cpp:19:
/Users/gremito/.vim/bundle/YouCompleteMe/third_party/ycmd/cpp/ycm/Utils.h:23:10: fatal error: 'filesystem' file not found
#include <filesystem>
         ^~~~~~~~~~~~
1 error generated.
make[3]: *** [ycm/CMakeFiles/ycm_core.dir/CandidateRepository.cpp.o] Error 1
make[2]: *** [ycm/CMakeFiles/ycm_core.dir/all] Error 2
make[1]: *** [ycm/CMakeFiles/ycm_core.dir/rule] Error 2
make: *** [ycm_core] Error 2
```

### older ycmd

ググったところ [osx 10.14.6 install latest version failure #3805](https://github.com/ycm-core/YouCompleteMe/issues/3805) を見つけて次のように最新バージョンでは macOS 10.15 以上でないと使えないと言われていました。

> We've moved to C++17 and are using std::filesystem, which is only available since macOS 10.15. For macOS 10.14, either check out an older YCM commit, or use an older ycmd commit.

[2021/02/23 19:42 のコメント](https://github.com/ycm-core/YouCompleteMe/issues/3805#issuecomment-784107020)で解決しました。

```shell
$ cd /path/to/YouCompleteMe
$ git fetch origin
$ git checkout legacy-c++11
$ git submodule update --init --recursive
$ ./install.py
```