OmniAICreator/Qiita-1.07M huggingface dataset:
【Ruby on Rails】フォームオブジェクトを使って検索キーワードをcontrollerに送る

## 想定対象者

- Ruby on Railsで検索フォームを独自で作りたい
- ransackを使いたくない
- Google Books APIを使ったポートフォリオを作りたい(本記事ではあんまり関係ないけど)

## (個人的に)実現したいこと
Google Books APIでは以下URLにキーワードを入れるだけで検索結果を返してくれるという仕様があります。
https://www.googleapis.com/books/v1/volumes?q=（ここにキーワード）

これを使えば、最終的に以下のようなViewの実装が思い浮かびます。
(ただし、この解説は別記事で予定)

```ruby:books_controller.rb
@books = 処理(keyword)
>> [book1, book2, book3, ....]
```

```slim:search.html.slim
= @books.each do |book|
  = image_tag book.image
  = book.titile
  = book.author
  = (略)
```
検索フォームを作るgemとしてransackという有名なgemがあります。
ただ、アプリのDB内で検索を行う目的で使われるもののようです。
本記事では、実際に検索を行うロジックはGoogle Books APIにキーワードを送るだけなので、ransackを使う必要がないことになります。
※まともにransackを使った経験があるわけではありません。

上記の実装をするには、まずは検索キーワードをcontrollerに持ってくる必要があります。
その際にはフォームオブジェクトを使うのが良いです。

## フォームオブジェクトとActiveModel

#### まず、フォームオブジェクト is 何

参考:
[form objectを使ってみよう](https://tech.medpeer.co.jp/entry/2017/05/09/070758)
[フォームオブジェクトでバリデーション　初心者→中級者へのSTEP2/25](https://qiita.com/kamohicokamo/items/d2ea4d71f86d99261b1a)
(他にもフォームオブジェクトで検索するとたくさん出てきます)

本記事に当てはめれば、

- form_withで検索キーワードだけを`books#search`に送る
- ActiveModelをincludeしたクラスを作る

ということになります。


### ActiveModel is 何

誤解を恐れずに言うと、ActiveRecordのDBと連携しない版

ActiveRecordはDBへの連携以外でも、validateだったり、attributeだったり、便利な機能があると思います。
DBへの連携は必要ないが、Viewに対するこれらの機能は使いたい、というケースの場合にはActiveModelを使うべき、ということになります。
検索キーワードをcontrollerに送りたいだけでDBとは連携させない、本記事のケースのような場合には適切です。

参考:
[【ruby】ActiveModelを使ってDBと関係ないFormを作成する【Rails】](http://tanihiro.hatenablog.com/entry/2014/01/09/193720)
[ActiveModel::Attributes が最高すぎるんだよな。](https://qiita.com/alpaca_taichou/items/bebace92f06af3f32898)

用法としては以下の通り。

1. app/forms/を作り、スネークケース.rbを置く
1. class キャメルケースでクラスを定義する
1. そのクラスにAcitveModelのモジュールをincludeする

参考: Railsガイドすごい!!!笑
[Active Model の基礎](https://railsguides.jp/active_model_basics.html)

## 実装

具体的に本記事のやりたいことに当てはめると、以下のコードになります。

```ruby:app/forms/search_books_form.rb
class SearchBooksForm
  include ActiveModel::Model
  include ActiveModel::Attributes

  attribute :keyword, :string
end
```

- `include ActiveModel::Attributes`をしたことでattributeメソッドを使えます。
- `attribute :keyword`と書けば、`@search_form.keyword`を使えます。

```slim:app/views/books/search.html.slim
= form_with model: @search_form, url: search_books_path, local: true, scope: q, method: :get do |f|
  .form-group
    = f.label :keyword, 'キーワード'
    = f.text_field :keyword, class: 'form-control', value: @search_form.keyword
  = f.submit '検索する'
```

```ruby:app/controllers/books_controller.rb
  def search
    @search_form = SearchBooksForm.new(search_books_params)
    # @search_form.keywordに検索キーワードが渡ったので、以下色々なことしちゃいましょう
  end

  private

  def search_books_params
    params.fetch(:q, keyword: '').permit(:keyword)
  end
```


### 説明する前に力尽きたこと

- 検索結果というリソースはURLとして持つべきという話
- なぜGETメソッド使うのか
- scope: :q is 何
- fetch is 何
- 「色々なことしちゃいましょう」の「色々なこと」(本当なら検索して結果をまとめるロジックを「色々なこと」に書きます)

参考:
[【Ruby on Rails】require と permit の使い方がよく分からない](https://techblog.kyamanak.com/entry/2017/08/29/012909)

あとでこの記事を修正して書くモチベが生まれるかもしれません~~し、生まれないかもしれません~~。

### 教えてほしいこと

- markdownでコード書いてるときに日本語部分の赤下線部破線を消す方法
- フォームオブジェクトの説明ってこれで合ってるでしょうか
初心者によるプログラミング学習ログ 254日目

#100日チャレンジの254日目
twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。
100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
254日目は、

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">おはようございます<br>254日目<br>・udemyでjavascript講座　インプット学習<a href="https://twitter.com/hashtag/%E6%97%A9%E8%B5%B7%E3%81%8D%E3%83%81%E3%83%A3%E3%83%AC%E3%83%B3%E3%82%B8?src=hash&amp;ref_src=twsrc%5Etfw">#早起きチャレンジ</a><a href="https://twitter.com/hashtag/%E9%A7%86%E3%81%91%E5%87%BA%E3%81%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%A8%E7%B9%8B%E3%81%8C%E3%82%8A%E3%81%9F%E3%81%84?src=hash&amp;ref_src=twsrc%5Etfw">#駆け出しエンジニアと繋がりたい</a><a href="https://twitter.com/hashtag/100DaysOfCode?src=hash&amp;ref_src=twsrc%5Etfw">#100DaysOfCode</a></p>&mdash; ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) <a href="https://twitter.com/yudapinokio/status/1235290536648609793?ref_src=twsrc%5Etfw">March 4, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
Courseraの上級機械学習講座でGoogle Colabの環境を整える方法

Courseraの上級機械学習のプログラミング課題でGoogleColabを使う方法です。私的メモも兼ねて。

# 経緯
2日ほど前から上級機械学習の講座を受講し始めたのですが、 3週目のプログラミング課題にてとうとう「Courseraが提供する環境でも実行できるけど、CPUだからモデルのトレーニングに2時間かかるよ!」と言われてしまったので、早々にGPUの使えるGoogleColaboratoryに切り替えました。

# Courseraのプログラミング課題提出の仕組み
Courseraの（というかこの講座の？）プログラミング課題では、チェックポイントごとに、変数の値(モデルの予測成績やパラメータの総数など)をCoursera側に送信して、値が許容範囲かどうかを確認することで得点がもらえるような仕組みになっています。大体一つの課題で6つくらいチェックポイントがあります。この時、grading_utils という独自のモジュールを使用しています。今回の主な目的はこのモジュールをColabから呼び出せるようにすることです。

# ファイルのダウンロード
実は、Colabで環境を構築できるように、Coursera側で"setup_google_colab.py"というファイルを用意してくれています。まずはこのファイルをダウンロードします。

1.まず、課題のノートを開いて、左上のCOURSERAのロゴをクリックします。
<img width="1252" alt="スクリーンショット 2020-03-05 3.26.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597096/97b650aa-7a66-a674-93be-5067fef6f82d.png">

2.するとファイルの一覧が表示されるので、"setup_google_colab.py"を選択して開きます。
<img width="1193" alt="スクリーンショット 2020-03-05 3.28.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597096/77b138aa-2865-7e88-bc55-17cc41092171.png">

3.左上のFileタブからDownloadを選択するとfileを自分のPCに落とすことができます。
<img width="1154" alt="スクリーンショット 2020-03-05 3.29.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597096/17479bdb-c745-065c-94f0-36aa9e2cd6be.png">

4.同様にして、課題の.ipynbも自分のPCに落とします。
5.落としてきたファイルを自分のgoogle drive上にupします。

# Google Drive のマウント
次に、Colabから自分のGoogle Drive上のファイルににアクセスできるようにDriveをマウントします。

1. まず、upした課題のファイルをColabで開きます。
2. 次のコマンドを実行します。

```
from google.colab import drive
drive.mount('/content/drive')
```
3.　実行すると、こんな感じでリンクが出てきます。リンクにアクセスして認証コードをゲットし、Enter your authorization code: に入力すればマウント完了です。
<img width="1381" alt="スクリーンショット 2020-03-05 3.45.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597096/c715d6bd-e5ff-7662-0030-b42f3155fcec.png">

4.横の'三'みたいなマークをクリックすると、'drive'ディレクトリの下に自分のドライブのファイルが入っているのが確認できると思います。
<img width="681" alt="スクリーンショット 2020-03-05 3.54.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597096/ed6d9566-ebab-ff2c-5cf3-7508264b1e31.png">

# Setup モジュールのimport
後は、パスを通してモジュールをimportするだけです。
例えば僕の場合、ファイルの置き場所は"My Drive/HOME/Cousera/setup_google_colab.py"なので、下のコマンドでsys.pathにパスが追加されます。

```
import sys
sys.path.append('/content/drive/My Drive/HOME/Cousera/')
```
これでimportできます。

```
import setup_google_colab
```

# Setup
setup_google_colabの中には、各課題ごとの設定用の関数が定義されていて、実行すると必要な外部モジュールや、ファイルを自動的にcolabのフォルダ上にダウンロードしてくれます。
今回は課題3なので、

```
setup_google_colab.setup_week3()
```
<img width="579" alt="スクリーンショット 2020-03-05 4.05.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597096/8b70b2af-4b5f-6ddd-cbf3-e1243c5fc011.png">

以上です。これで、課題のノートがColab上で動くようになります。

# 最後に
結局、この後モデルの構造を間違えて3回くらいトレーニングし直したので、CPUでやってたらと思うと冷や汗です。ちなみにノートにはCPUだと1時間30分かかると書いていたのですが、GPUだと4分強で計算が終わりました。さすが、、、
Best 65+ Informatica Interview Question and Answers in 2020

Here, is the list of mostly asked Informatica Interview Questions and this list includes two types of questions- scenario-based Informatica questions and advanced Informatica questions. Nevertheless, all these questions are suitable for beginners, intermediates, or advanced Informatica learners to crack the job interview. 


 click here to read more
https://www.positronx.io/best-informatica-interview-question-and-answers/
React Native vs. Ionic – A head-to-head Comparison in 2020

 In this article, we are going to look at an intense comparison between Ionic and React Native development frameworks. At the end of this article, you will be all clear about what framework you should use for your development projects. 

click here to read morehttps://www.positronx.io/react-native-vs-ionic-head-to-head-comparison/
オンライン学習・ミニバッチ学習・バッチ学習の繋がり

## はじめに
1度に学習するサンプル数を決めるとき，```batch_size```の変化でオンライン学習，ミニバッチ学習，バッチ学習と名前が変わって繋がりが分かりずらかったので図に残しておきます．

## オンライン学習・ミニバッチ学習・バッチ学習の関係性
サンプル数を$N$とするとオンライン学習・ミニバッチ学習・バッチ学習の関係性は以下の図のように表すことができます．
![論文の挿入画像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182036/7a1a3ef0-7276-d6e6-6379-f68386ede6a4.png)
オンライン学習，ミニバッチ学習，バッチ学習と3種類ありますがこれは```batch_size```によって名前が変わっているだけです．
Top 40+ HTML5 Interview Questions and Answers in 2020

 In case you don’t know much about HTML and are going to appear for a programming job interview, then hold on. It would be a disgrace if you would not be able to answer HTML 5 questions in the interview, and this can cost you your future job. 

click here to read more
https://www.positronx.io/top-html5-interview-questions-and-answers/
githubの初期環境セットアップ

# githubの初期環境セットアップ
別の環境でセットアップするたびに「なんだっけ」ってなってたのでまとめた。ほぼ個人用メモに近い。

実務でgithubもSSHも利用してないプヨグラマなので、
より良い方法があれば、コメントしていただけると助かります。


## 0. 環境について
Windows 10

## 1. git install
https://git-scm.com/download/win でインストーラが落ちてくる。
適当にインストール。

## 2. git --global config

**1. グローバル設定**

```cmd
git config --global user.name "Takumi Akashiro"
git config --global user.email "your_email@example.com"
git config --global core.editor code
```

**2. 設定の確認**

```cmd
git config --list --global
```

**2. 確認結果**

```
> git config --list --global
filter.lfs.required=true
filter.lfs.clean=git-lfs clean -- %f
filter.lfs.smudge=git-lfs smudge -- %f
filter.lfs.process=git-lfs filter-process
user.name=Takumi Akashiro
user.email=your_email@example.com
core.editor=code
```

## 3. SSH 設定

**1. ssh鍵の作成**

```cmd
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
```

色々聞かれる。

```
// keyの保存場所。
Enter file in which to save the key (/Users/you/.ssh/id_rsa): [Press enter]
// パスフレーズ。2回聞かれる。
Enter passphrase (empty for no passphrase): [Type a passphrase]
```

**2. 公開鍵をクリップボードにコピー**

```cmd
clip < "%userprofile%/.ssh/id_rsa.pub"
```

**3. https://github.com/settings/keys で登録**

**4. 確認**

```
git clone git@github.com:%MY_ACCOUNT%/%MY_REPOSITORY%.git
```
パスフレーズとか聞かれるはず。毎回入力するのも面倒なので、SSH-Agentに任せる。

## 4. SSH-Agent 登録

**1. PowerShellを管理者権限で起動し、以下を実行**

```PowerShell
Set-Service -Name ssh-agent -StartupType Manual
Start-Service ssh-agent
```

**2. 適当なコマンドプロンプトで以下を実行**

```cmd
ssh-add "%userprofile%/.ssh/id_rsa"
```

**3. git configを更新。**

これを忘れているとWindows10で標準で入ったOpenSSHが効かない(二時間浪費)

```cmd
git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"
```

## 参考
[Connecting to GitHub with SSH - GitHub Help](https://help.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh)

[お前らのSSH Keysの作り方は間違っている - Qiita]
(https://qiita.com/suthio/items/2760e4cff0e185fe2db9)
[Win10でssh-addがError connecting to agent_ No such file or directory - Qiita](https://qiita.com/yuta0801/items/d65f1fc3115773861283)


[VSCodeからSSH経由でPush - reincarnation+#Tech](https://reincarnation-plus.gitbook.io/project/other/push_github_ssh)
core.sshcommandを更新しないと動かないことを示した神。ありがとうございます！……
[Django]startprojectをした時にカレントディレクトリ下に無駄にディレクトリが作られることを防ぐ

##はじめに

Djangoでstartprojectをした時にカレントディレクトリ下に無駄にディレクトリが作られてしまう。。。ということないでしょうか。

私はよくDjangoで新しいアプリを作り始める時に、まずディレクトリを作ってそこに移動してから、startprojectします。

```
# Desktop上にmysiteディレクトリを作成し、その中でプロジェクトを開始している
$ cd ~/Desktop
$ mkdir mysite 
$ cd mysite
$ django-adimin startproject mysite
```

しかし、こうしたやり方でプロジェクトをスタートするとこのように無駄にディレクトリが作られてしまうことになります。

```
# mysiteディレクトリの中に無駄にまたmysiteが出来てしまっている
mysite/
└── mysite
    ├── mysite
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    └── manage.py
```
そもそもなぜこんなことが起こるのでしょうか。またどうすればこのような無駄なことを避けられるのでしょうか。

##何はともあれ公式を読んでみた

Djangoのstartprojectの公式ページを読んでみます。
下記の英文ドキュメントを僕の方で意訳してみました。
[Django/startproject](https://docs.djangoproject.com/ja/3.0/ref/django-admin/#startproject)

---
**django-admin startproject name [directory]**
(このコマンドは)カレントディレトリもしくは指定された場所の中で、与えられたプロジェクトネーム向けにプロジェクトのディレクトリ構造を作ります。
デフォルトで、新しいディレクトリはmanage.pyと(setting.pyやその他のファイルを含む)プロジェクトのパッケージを内包しています。
**もしプロジェクトネーム(例えばmysiteというプロジェクトネームとする)のみが与えられると**、プロジェクトのディレクトリとプロジェクトのパッケージの両方がプロジェクトネームと同じmysiteと名付けられ、**プロジェクトディレクトリはカレントワーキングディレクトリに作られるでしょう。**
もし保存場所の指定が行われたなら、Djangoはプロジェクトのディレクトリとして存在しているディレクトリを使うでしょう。そしてその中にmanage.pyとプロジェクトパッケージを作ります。ドットを使うと現在のワーキングディレクトリを示します。

---

~~僕の和訳の稚拙さもあいまって~~何を言っとるんじゃという話なので少し補足をします。

まず、django-admin startproject name [directory]でnameでプロジェクト名、directoryでプロジェクトを始める場所を指定してディレクトリ構造を作ってくれます。

ディレクトリ構造というのは、要はコードツリーの中で示したmanage.pyとmysiteディレクトリのことです。mysiteディレクトリというのがプロジェクトパッケージというやつです。

さて、ここで保存先の[directory]を指定してしないとどうなるのかも公式は丁寧に教えてくれています。
カレントワーキングディレクトリ(自分が今いるディレクトリのことですね)にプロジェクトディレクトリが作られた上でその中にmanage.pyとプロジェクトパッケージが作られます。ここで意図せず無駄なディレクトリが出来ていたわけです。

##解決策

解決策は単純で、以下の2つのうちいずれかをとれば良いです。

- 最初からディレクトリを作ってその中でstartprojectする、ということを辞める
- django-admin startproject name . のようにドットでカレントディレクトリに作りますよーと宣言する

本日は以上です。

###環境
MacOS
Python 3.7.5
Django 3.0.3

本記事は筆者本人が[Zenn](https://zenn.dev/kumamoto/articles/f3520fb52f5dc6)にも転載済です。
インテルOptaneメモリーを使っているハードディスクドライブをどうしてもデフラグしたかった話

# 要点

Optaneメモリーと一体化してるHDDをどうしてもデフラグしたいときは、いったん一体化を無効化するといいよ。

# もう少し詳しく

[インテルOptaneメモリー](https://www.intel.co.jp/content/www/jp/ja/architecture-and-technology/optane-memory.html)とは、HDDの読み込み動作速度を向上させるために使うことのできるキャッシュメモリー(のようなもの)である。有効化すると、WindowsからはHDDと**一体化したSSDとして認識**される。

さて、Windows10ではSSDにはデフラグが働かない[^1]。上で述べたようにOptaneを有効化したHDDはSSDとして認識される。ということは、使い方によってはHDD部分の断片化が進んでシステムのパフォーマンスが落ちるかもしれない[^2]。手動でデフラグをしようとしてもダメである。
![defrag1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43042/c42e5df2-8951-167b-147d-b8ea0f0476e3.png)

完全にSSDだと思われている。このままでは埒が明かないのでOptaneを有効化したHDD(のHDD部分)をデフラグするにはどうすればいいかを調べた結果、どうやら以下の手順を取るしかないようである。

1. Optaneを無効化する。
2. 再起動の後、一体化の外れたHDDをデフラグする。
3. Optaneを有効化する。

無効化するとちゃんとHDDとして認識されるのでデフラグができる[^3]。

![defrag2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43042/d36b8617-ab98-1d3c-6d51-38453e1830bc.png)

当然である。

というかインテルのフォーラムにも書いてあった：

- [Defragging an Optane Drive - Intel® Community Forum](https://forums.intel.com/s/question/0D50P000049rNnlSAE/defragging-an-optane-drive)

> You could once a year disable Optane and perform a complete system cleanup and then turn it back on but that sounds like a big PITA to be honest.
>
> (意訳) 一年に一度くらいOptaneを無効化して完全にシステムをクリーンアップしてまた有効化するって手もあるけど、それってぶっちゃけ面倒だよね。

ついでに言うと、Optaneを無効化して有効化すると当然キャッシュの内容は消えるので注意。

[^1]: トリムだけされる。
[^2]: 一年ほどWSLからガンガン読み込み書き込みしてたらものすごく遅くなった。用途に応じて、HDD+Optaneではなく素直にSSD買った方が良い、という教訓である。
[^3]: ちなみにデフラグ後、本当にディスクアクセスのパフォーマンスが戻ったかどうかはまだ様子見中。気のせいかもしれないし、普通に経年劣化かもしれないし、バックグラウンドで勝手に動いてるプリインストールアプリのせいかもしれないし、Windows Updateのせいかもしれない。Windowsの世界は奥が深い。
【Go言語】FirebaseAuthのユーザーを全て削除する

# 概要

FirebaseAuthを検証で使ってたら `example1@example.org` みたいなユーザーが大量に増えてしまったので一括で削除を行いたいが、Firebaseのコンソールからは出来ないみたいだったので  Firebase Admin SDK で一括削除を行うことにした。該当記事が見つけられなかったので自分用メモも兼ねて記事を書いた。

今回は Go 言語で削除処理を書いたが、下記の言語も対応している模様。

- Node.js
- Java
- Python
- C#

参考: [サーバーに Firebase Admin SDK を追加する](https://firebase.google.com/docs/admin/setup?hl=ja#next_steps)

# 前準備

1. `go get firebase.google.com/go` で Firebase Admin SDK を導入
2.  Firebaseコンソールからサービスアカウントキーを取得する

サービス アカウント用の秘密鍵ファイルを生成するには:

```
1. Firebase コンソールで、[設定] > [サービス アカウント] を開きます。
2. [新しい秘密鍵の生成] をクリックし、[キーを生成] をクリックして確定します。
3. キーを含む JSON ファイルを安全に保管します。

※ https://firebase.google.com/docs/admin/setup より引用
```

# 実装

全ユーザーを一括削除するAPIは存在しなかったので、全ユーザーを取得してイテレータで1つずつ削除処理を行った。
参考: [ユーザー管理  |  Firebase](https://firebase.google.com/docs/auth/admin/manage-users)

```go
package main

import (
	"context"
	"firebase.google.com/go"
	"firebase.google.com/go/auth"
	"google.golang.org/api/iterator"
	"google.golang.org/api/option"
	"log"
)

func main() {
	// Firebaseと接続する
	opt := option.WithCredentialsFile("./サービスアカウントキー.json")
	app, err := firebase.NewApp(context.Background(), nil, opt)
	if err != nil {
		log.Fatalf("error connect firebase: %s\n", err)
	}

	// FirebaseAuthクライアントを取得
	client, err := app.Auth(context.Background())
	if err != nil {
		log.Fatalf("error connect firebase auth: %s\n", err)
	}

	// 全てのユーザーを取得する
	iter := client.Users(context.Background(), "")
	for {
		user, err := iter.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.Fatalf("error listing users: %s\n", err)
		}
		log.Printf("read user email: %v\n", user.Email)

		// ユーザーを削除する
		err = deleteUser(client, user.UID)
		if err != nil {
			log.Fatalf("error deleting user: %s\n", err)
		}
	}
}

// ユーザーを削除する
func deleteUser(client *auth.Client, uid string) error {
	err := client.DeleteUser(context.Background(), uid)
	if err != nil {
		return err
	}

	log.Printf("Successfully deleted user: %s\n", uid)
	return nil
}
```

# まとめ

Authユーザーの一括削除は開発者にとって需要が高いと思うので、この記事が誰かの力になれたらと思います。
セキュリティ的にマズい、コードの書き方に問題がある等がありましたらコメントでご教示頂けますと幸いです。
【Git】Gitよく使うコマンド個人メモ


## ディレクトリ毎回打ち込むのめんどくさ
ターミナルで上矢印キー押したらシャットダウンしても前回のコマンド辿れる

## リポジトリ作成からPushまで

```
cd <任意のディレクトリ>
git init
git remote add origin <リモートのURL>
git add .
git commit -m "Commit Message"
git push -u origin master
```

## 今どんな状態か確認

```
git status
```

## ブランチ作成してリモートに登録

```
git checkout -b <任意のブランチ名>
git push -u origin <任意のブランチ名>
```

## ブランチ削除

```
git branch -D <任意のブランチ名> //ローカル削除
git push --delete origin <任意のブランチ名>　//リモート削除
git fetch -p　//GUI上で削除したリモートをローカルに反映
```


## コミットのコメント修正

```
git commit --amend -m "新しいコメント"
```

## ローカルでごちゃごちゃいじったものを元に戻す

```
git checkout .
```

## 過去のごちゃごちゃした履歴を見る

```
git reflog　//qで閉じる
```

## 過去のコミットの履歴を見る

```
git log --oneline
```

## 過去のコミットの状態まで戻る

```
git reset --hard <commit ID>　//hardだとそのコミット自体も消える
```

## 特定のブランチを指定してクローン

``` 
git clone -b ブランチ名 リポジトリのアドレス
```

## ルートを任意の名前でクローン

``` 
git clone リポジトリのアドレス　任意の名前
```

## マージ

``` 
git checkout master　//ここに移動する前にちゃんとコミットしとく
git merge develop
git push origin master　//Githubに反映
```

## リモートのURL変更

```
git remote set-url origin {new url}
```

## 途中から.gitignoreを追加したときに反映させる

```
git rm -r --cached .
```

## リモートの登録やり直し

```
git remote rm origin
```

## 特定のファイルだけインデックスに追加

```
git add -n . //変更を表示
git add hoge huga
```

## 最新のコミットを取り消し　ファイルは残す

```
git reset --soft HEAD^
```

## 検索

```
git grep {検索ワード}
git grep -w {検索ワード}  //完全一致時のみ表示
```

## 強制Push

```
git push -f
```


## Cherry Pick

```
git checkout develop  //欲しいコミットがあるdevelopブランチに切り替え

git log -oneline -1 //最新の1件のコミットを確認
123456a コミットのコメント

git checkout hoge-branch //該当ブランチに切り替える

git cherry-pick 123456a //特定のコミットのIDを指定
```
[Rust] 文字列リテラル: エスケープあるいは raw string

ソースコード中で文字列を定義する際に, 通常の `"..."` という形ではいくつかの文字はエスケープする必要があります. 一方 raw string を用いれば書いたままの文字列と解釈されます.

なお本記事は [Tokens - The Rust Reference](https://doc.rust-lang.org/reference/tokens.html) を要約したものです. むしろ原文よりわかりにくくなった気もしますが...

# 文字列

## String

文字列リテラルは二重引用符内に書きます (例: `"Hello, world!"`)が, 当然引用符をはじめとするいくつかの文字についてはエスケープが必要です. でなければ引用符を含む文字列リテラルが定義できません. 具体的には次のものに注意します (なおこれは char と共通です).

* ユニコード符号位置: `\u{32FF}` のようにユニコード符号位置を直接指定する.
* ASCII エスケープ: `\x41` のような ASCII 符号, `\n`, `\r`, `\t`, `\\`, `\0`.
* 引用符: `\'` および `\"`.

なお, `\` 直後の改行およびインデントは無視されます.

```rust
assert_eq!("foobar", "foo\
                      bar");
```

## Raw string

一方, raw string を用いれば __エスケープが一切必要ない, 書いたままの文字列__ として解釈されます. これは, 引用符の前に `r` + 0 個以上の `#` を配置し, raw string が終了したら引用符に加えて同じ個数の `#` を置くことによって定義されます. raw string の前後につける `#` の数は任意なので, 文字列中に登場する連続する `#` の最大数より多くします.

* `r"Hello, world!"` -> `Hello, world!`
* `r#"これ"は引用符"#` -> `これ"は引用符`
* `r##"#06-6012-3456"##` -> `#06-6012-34567`

# バイト列

バイト列 `[u8]` は char/str リテラルに接頭辞 `b` を設けることで宣言できます. ただし ASCII 文字しか使えません. 対応して, ユニコード符号位置を除く上述のエスケープを使用することができます. ひとつおもしろい点として, 文字列では `"\x41"` は ASCII 符号でしたから 0x7F が上限でしたが, いまはバイト `u8` として解釈するため 0xFF まで使えます (例えば `b"\xFF"`).

さらに, バイト列に関しても raw bytes が存在し, 例えば `br##"#06-6012-3456"##` は有効な `[u8]` リテラルの宣言です.
bash: code: command not foundと言うエラーが出た話

# 目的

- ターミナルからファイルをVScodeで開く際に出たエラーの解決法を記載する。

# 実施方法

1. codeコマンドのインストール
    1. VScodeのウインドウを開く
    1. 「command」+「shift」+「p」を同時に押下してコマンドウインドウを開く。
    1. コマンドウインドウに下記を記載する。

        ```text
        Shell Command: Install 'code' command in PATH
        ```

    1. 入力後、表示される「codeコマンドをインストールする」をクリックする。

        ![スクリーンショット 2020-03-04 21.05.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/f6378f25-b2cb-7ab6-9ecb-519a5a2582d7.png)
1. 確認
    1. ターミナルを再起動して`code`コマンドを実行する。
【Swift】Firebaseのuser.photoURLをカメラロールの画像をアップして設定する


カメラロール（ローカルファイル）から写真をピックし、ローカルのURLを指定してStorageにアップロード。その際のDownloadURLをuser.photoURLに指定してやる。

```swift

import UIKit
import Firebase
import FirebaseDatabase
import FirebaseStorage

class EditProfileViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {    
    var user: User!
    var ref: DatabaseReference! //
    private var databaseHandle: DatabaseHandle! //
    var storage: Storage!
    var storageRef: StorageReference!
    var localImageURL: NSURL?

@IBAction func uploadImageButtonDidTap(_ sender: Any) {
        let ipc = UIImagePickerController()
        ipc.delegate = self
        self.present(ipc, animated:true, completion:nil)
    }
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        picker.dismiss(animated: true, completion: nil);
        self.profileImageView.image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
        self.localImageURL = info[UIImagePickerController.InfoKey.imageURL] as? NSURL
    }


@IBAction func saveDidTap(_ sender: Any) {
        let changeRequest = user.createProfileChangeRequest()
        guard let imageURL = localImageURL else { return }
        let localFile = imageURL as URL // ここは公式ドキュメントに揃えただけなので別に変数化しなくていい
        let imageRef = storageRef.child("user/\(user.uid)/images/profile.jpg")
        _ = imageRef.putFile(from: localFile, metadata: nil) { metadata, error in
            guard let metadata = metadata else {
                // Uh-oh, an error occurred!
                return
            }
            // Metadata contains file metadata such as size, content-type.
            let size = metadata.size
            // You can also access to download URL after upload.
            imageRef.downloadURL { (url, error) in
                guard let downloadURL = url else {
                    // Uh-oh, an error occurred!
                    return
                }
                changeRequest.photoURL = downloadURL
                changeRequest.commitChanges() { (error) in
                    self.dismiss(animated: true, completion: nil)
                }
            }
        }

    }
}
```

https://firebase.google.com/docs/storage/ios/create-reference?hl=ja
https://firebase.google.com/docs/auth/ios/manage-users?hl=ja#update_a_users_profile
https://stackoverflow.com/questions/40177250/firebase-how-to-get-image-url-from-firebase-storage
GuardDutyのイベントを通知する際の注意点（2020/03/06追記しました）

クロスアカウント環境下にて、GuardDutyをONにしておき
集約するアカウントをGuardDutyのアカウント機能でMasterとして
それ以外のアカウントのGuardDutyをMemberと設定している場合。

上記のような環境は良くある構成だと思いますし、以下公式にもある通り
MasterのアカウントにおいてCloudWatchEvents(以下「CWE」）と
Lambdaで通知を実装しているかと存じます。
https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings_cloudwatch.html

その場合、CWEでハンドルできるのは集約アカウント上のGuardDutyが
検知したイベントのみです。Memberとしたアカウント上のGuardDutyイベントは
マネジメントコンソール上は参照可能ですが、CWEでハンドルできません。

GuardDutyでMaster-Memberの制御を入れればMaster側にイベントが集約できるかと
思っていましたが、各アカウントごとにCWEを設定する必要があります。

**集約アカウントにおけるCWEのRule**

```
{
  "source": [
    "aws.guardduty"
  ],
  "detail-type": [
    "GuardDuty Finding"
  ]
}
```

**Memberアカウント側でサンプルイベント作成**
![Screen Shot 2020-03-05 at 1.08.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110457/c91b30f2-5009-3367-b829-dbbf3cd384ae.png)

**Masterアカウント側のCWEは上記サンプルイベントを検知できず・・・**
![Screen Shot 2020-03-05 at 1.28.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110457/38f8b165-1e58-1b7b-3de8-b6facd922d68.png)



先日、GuardDutyにS3へ書き込みする機能がリリースされました。クロスアカウント環境において
GuardDutyの通知も集約したい場合は集約アカウント上にバケットを作成し
各アカウントからそのバケットに対してイベントをPUTして、バケットのPUTイベントを
Lambdaでハンドルして読み込み／解析するのが一番単純な形かと存じます。

## 2020/03/06 追記しました
試したところメンバー側のイベントをマスター側のCWEで取得できるようになりました。

**メンバー側でイベントを起こす**
![Screen Shot 2020-03-06 at 7.42.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110457/eed4a167-b229-2c6f-28c0-d5709c00128a.png)

**マスター側のCWEで取得できる**
![Screen Shot 2020-03-06 at 7.47.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110457/56a8ec42-1066-16cd-3873-b2504a4dbdc7.png)

クロスアカウント環境下においてCWEでGuardDutyのイベントを取得しようとした場合
マスターアカウント分しか取得できない、使うべきでないと書きましたが
現時点ではメンバーアカウントのイベントも取得できるようになったようです。
Go言語 + GVM + GoModules + GoLandを利用する際の初期設定

# 概要

Go言語でパッケージ管理をGo Modules、IDEでJetBrains製のGoLandを利用する際の初期設定についての自分用メモ。1年前(2019年春)から始めた構成なのでもしかしたらもう古いかもしれない。

gvmはすでにインストール済みとする。

# GoModuleの有効化

Goバージョンは1.12以上ならなんでも良い(はず)。

1. `gvm use go1.12` で Go 言語のバージョンを指定
2. `export GO111MODULE=on ` で Go Modules 有効化 (.zshrcとかに書いとくと良い)
3. `git init` で git 管理対象にすることで、`go mod` にモジュール名を認識させる
4. `go mod init` で Go Modules の初期化

# GoLandの設定

1. GoLand 2018.3.4 以上をインストール
2. Preferences を開いて、Go > Go Modules(vgo) を有効化する。(下記画像の通り)
3. [vgo integration support | GoLand Blog](https://blog.jetbrains.com/go/2018/05/18/vgo-integration-support/) のようにパッケージのインストールを行う (普通に `go get` しても良い)

[![Image from Gyazo](https://i.gyazo.com/31518cc0f3602e561b377229605999b1.png)](https://gyazo.com/31518cc0f3602e561b377229605999b1)
もうわたし苦労しない。。 Pythonの標準ライブラリを使ってマルチプロセス。

## 概要
マルチプロセス、簡単にやりましょう。
でも、ちょっとやり方に詰まって結局時間節約できなかった。。？？　

もぅﾏﾁﾞ無理。。。。。普通にfor文まわそ。。。。

そういう人に読んでもらいたい。


Pythonの標準装備であるProcessPoolExecutorとfunctools.partial をつかって、快適にマルチプロセスで並列計算しましょう。
使えるものは、使わないと損だよね？？

## 状況説明

```test.py

def my_function(arg1):
    for i in range(100):
        'めっちゃヘビーな処理ですのん'
        result_list.append(some_operation(i))

if __name__ == "__main__":
    my_function()
```
こういう書き方、けっこう何も考えずに書きますよね？
こういうとき、１００回の処理あるけど、マルチプロセスで簡単に並列処理したい。。。



## 簡単なやり方
1. 関数をちょろっと書き直します。
2. ProcessPoolExecutor をインポート。
3. functools.partial　をインポートし関数をマッピング。


### 1. 関数をちょろっと書き直します。
先ほどの　```my_function```を、こんな風に書きます。

```test.py
def my_function(index, arg1):
    return some_operation(index)
```
つまり、ループを書くんじゃなくて、インデックスを渡して、一個一個処理するみたいなイメージ。

### 2. ProcessPoolExecutor をインポート。

まず、以下のように書きます。

```test.py
import os
from concurrent.futures import ProcessPoolExecutor

max_workers = os.cpu_count() or 4
print('=====MAX WORKER========')
print(max_workers)

with ProcessPoolExecutor(max_workers=max_workers) as executor:
```
```max_workers``` は、使用可能なプロセス数が格納されます。もし格納できない場合、この例では```4```が入ります。

### 3. functools.partial　をインポートし関数をマッピング。
つづいて、こんな風に書きます。

```test.py
import functools

with ProcessPoolExecutor(max_workers=max_workers) as executor:
    result_list = list(executor.map(functools.partial(my_function, arg1), range(100)))
    executor.shutdown(wait=True)
```

これだけです。
少し解説すると、今までループで書いて、```result_list```にアペンドしていた操作は、
```my_funtion```にインデックスを渡すことで、各インデックスでのオペレーションを複数プロセスに分けて実行するため。

```functools.partial``` は、関数（my_function）の一部の引数を変えて、引数の変わったそれぞれの関数を関数のイテレータとして返します。このとき、```my_function(index, arg1)``` のように、変更される引数は１つであり、それが関数の第一引数となっていることに**注意!!**

さらに、executor.mapにより、その```my_function```に渡される第一変数であるindexは、イテレータであるrange(100)のそれぞれで並列実行され、各実行結果がlistに格納されます。
これはまさに、```result_list```に各ループでアペンドしていた時と一緒です。


簡単なexampleコードはこちら

```test.py
from concurrent.futures import ProcessPoolExecutor
import functools


def test(arg1, index):
    print(f"index: {index}")
    return index + arg1


if __name__ == "__main__":
    arg1 = 1
    with ProcessPoolExecutor(max_workers=4) as executor:
        result = list(executor.map(functools.partial(test, arg1), range(10)))
        # result = list(executor.map(test, range(10)))
        print(list(result))

```


## まとめ
少しでも時間を節約するためにやりたいマルチプロセス。マルチプロセスの書き方で苦労して結局時間めっちゃかかった、、みたいなことになるの、辛いですよね。。。
今回は自分的にかなり簡単にPythonの標準ライブラリだけをつかって書けるマルチプロセスについて紹介させていただきました！！

おわり。
ALB使用時、ApacheのアクセスログにクライアントIPを記録する

# ALB使用時、ApacheのアクセスログにクライアントIPを記録する

EC2(Apache)の前段に、ALBを配置する構成はよくあると思いますが、
デフォルトの状態だと、ApacheのAccessログに、ALBのローカルIPが記述されてしまいます。
本来のアクセス元の情報を、Accessログに記録する場合のApache設定方法を記載します。

# 設定方法(Apache)
httpd.conf のログ出力設定を変更します。
ログフォーマット設定に「"%{X-Forwarded-For}i」を追記することで、
本来のアクセス元のIP(ClientIP)を記録させることが出来ます。

```
httpd.conf (Befor)
<IfModule log_config_module>

    LogFormat "%h %l %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%h %l %u %t \"%r\" %>s %b" common

```

``` 
httpd.conf (After)
<IfModule log_config_module>

    LogFormat "%{X-Forwarded-For}i %h %l %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%{X-Forwarded-For}i %h %l %u %t \"%r\" %>s %b" common

```

※common については、必要であれば追記願います。


# 設定変更後

httpdをリスタートし、設定情報を反映しましょう

```
# systemctl restart httpd
```

# おまけ
ALBからのヘルスチェックは、30秒おきにログに登録され
ログ解析の際に邪魔になるのでAccessログに記載しないようにしましょう。

```
httpd.confの<IfModule log_config_module>へ下記を追記

SetEnvIf User-Agent "ELB-HealthChecker.*" nolog
CustomLog "/var/log/httpd/access.log" combined env=!nolog
```

# 参考
[Amazon公式](https://aws.amazon.com/jp/premiumsupport/knowledge-center/elb-capture-client-ip-addresses/)
[ 簡単 ] aliasで自分だけのコマンドを作ろう

### 今回使うもの

・ターミナル
・vim


### alias 登録までの流れ

1. vimで.zshrcファイルを開く
2. aliasを定義する
3. 変更を適用させる

### 1. vimで.zshrcファイルを開く

```:ターミナル

$ vim ~/.zshrc

*場所はどこでも大丈夫です。
```

### 2. aliasを定義する

> ＊ここでは [vimコマンド](https://qiita.com/hide/items/5bfe5b322872c61a6896) を使用します。

まず、コマンド `i` で `INSERT`モード に切り替えます。

```:.zshrc



-- INSERT --
*INSERTモードになっているか確認
```

次に alias を定義します。

```:.zshrc

alias ss='cd ~/Desktop/screenshot'

*Desktopにあるscreenshotディレクトリへ移動するcdコマンド ｢cd ~/Desktop/screenshot｣ を ｢ss｣ と定義しています。
*[ss]の部分は自分の好きに変えることができます。
```

入力が終わったら、`escape` を押して、`:wq` で変更を保存します。

### 3. 変更を適用させる

```:ターミナル

$ source ~/.zshrc

*初めのコマンドの vim を source に変えるだけです。
```

これで alias の定義完了です。
早速ターミナルで使ってみましょう！
---

```:例：ターミナル

~ $ ss
*enterを押すと・・・

screenshot $ 
*どこにいても ~/Desktop/screenshot へ移動することができる
```

aliasの定義の仕方さえ覚えれば、あとは自由に専用コマンドを作成できます。打つのが面倒なデプロイ時のコードや、ローカルサーバーを立ち上げる`rails s` でさえ短縮させることが可能です。

---

#### 〜定義する時のポイント〜
・cdコマンドの場合は `絶対パス ([ ~/ ]で始まるバスの表記)` で定義する。（こうすることで自分がどの場所にいても使えるようなる）
・変更の適用はターミナルを再起動することでも可能なようです。（以下リンク参照）


参照リンク
---
・[エイリアス(alias)とは](https://wa3.i-3-i.info/word1149.html)
・[【初心者向け】エイリアスの設定方法](https://qiita.com/yutat93/items/b5bb9c0366f21bcbea62)
・[新しいコマンドも作れる！aliasコマンド詳細まとめ【Linuxコマンド集】](https://eng-entrance.com/linux-command-alias)
WindowsでPyTorch 1.5をCUDA 10.2向けにビルドする手順

Windows 10でPyTorch 1.5をCUDA 10.2向けにビルドする手順をのせておきます。（2020/3/1時点）
PyTorchと併せてよく使われるtorchvisionのビルド手順も含んでいます。
ここでは細かい説明は省略しているので、もう少し細かい説明が必要な場合は[Blog - dev.infohub.cc](https://dev.infohub.cc/build-torch15-cuda102-win10/)も参考にしていただければと思います。

#ビルド環境
- ハードウェア
    - CPU: AMD Threadripper 3960X
    - GPU: nVIDIA TITAN RTX
- ソフトウェア
    - Windows 10 Pro Version 1909 (x64)
    - Microsoft Visual Studio Community 2019 – Ver 16.4.5
    - Git for Windows（gitコマンドが使えれば何でもOK）
    - Python 3.8.2（AnacondaではなくPython.orgの方）
    - CUDA 10.2
    - cuDNN 7.6.5
    - Intel MKL 2019.0（AMDのCPUだけど強制的にMKLを使用）

#環境変数などの設定
- MKL_DEBUG_CPU_TYPE=5 （AMD系CPUでMKLを使うための設定）
- 他CUDA等へのPATHが通っていること

#PyTorch、torchvisionのビルド
Visual Studio 2019の```x64 Native Tools Command Prompt for VS 2019```を開いて以下の手順で作業を行います。（説明はコメント形式で載せています）

```docker
# ビルド用の仮想環境(build_pt)作成
python -m venv g:\work\build_pt
g:\work\build_pt\Scripts\activate.bat
cd /d g:\work\build_pt

# パッケージなどの更新
python -m pip install --upgrade pip

# ビルドに必要なパッケージの導入
pip install numpy ninja pyyaml mkl mkl-include cmake cffi wheel

# ソース取得
# 本家サイトの手順とは違って、サブモジュールは後から取得する
git clone https://github.com/pytorch/pytorch
cd pytorch

# 2020/03/01 13:24 時点の状態に切り替え（今回試した最新の状態）
git checkout ace2b4f

# サブモジュールの取得
git submodule sync
git submodule update --init --recursive

# ビルド（Threadripper3960Xで18分くらい）
python setup.py install

# wheelパッケージ作成（pipでwheelを入れておく）
# G:\work\build_pt\pytorch\dist に「torch-1.5.0a0+ace2b4f-cp38-cp38-win_amd64.whl」が作成される
python setup.py bdist_wheel

# ---- ここからtorchvisionのビルド ----------------------------------

# 親フォルダ（g:\work\build_pt）に移動
cd ..

# torchvisionのビルドに必要な追加パッケージの導入
pip install six pillow

# torchvisionでCUDAが使われるかどうかの確認
# この結果がTrueならCUDAが使われる（強制的に使うようにビルドすることも可能。詳細はtorchvisionのページを参照）
python -c "import torch; print(torch.cuda.is_available())"

# torchvisionのソースコード取得
git clone https://github.com/pytorch/vision.git
cd vision

git checkout b2e9565

# ビルド（数分くらい）
python setup.py install

# wheelパッケージの作成
python setup.py bdist_wheel

```

これでdistフォルダにPyTorchとtorchvisionのwheelファイル（*.whl）が作成されます。

#利用環境への導入
ビルドしたPyTorchとtorchvisionのwheelパッケージを、個別の仮想環境に導入する手順です。
ビルド用のフォルダはディスク容量をかなり使うので、実際にPyTorchなどを利用する環境と、ビルド用の仮想環境は分けておくと便利です。
ここでは、```c:\venvs\```配下に```ml```という仮想環境を作成する想定です。
通常のコマンドプロンプトから実行すればOKです（Visual StudioのコンソールでなくてOK）

```docker
# 仮想環境(ml)作成
python -m venv c:\venvs\ml
c:\venvs\ml\Scripts\activate.bat
cd /d c:\venvs\ml

# パッケージなどの更新
python -m pip install --upgrade pip

# PyTorchとtorchvisionの導入（依存パッケージも）
# PyTorchとtorchvisionのwhlファイルは上記で作成したファイルを指定
pip install numpy mkl six pillow
pip install "torch-1.5.0a0+ace2b4f-cp38-cp38-win_amd64.whl"
pip install "torchvision-0.6.0a0+b2e9565-cp38-cp38-win_amd64.whl"
```

これで、CUDA 10.2向けのPyTorch 1.5.0a0環境作成が完了です。
AWS Lambda の実行環境でデフォルトでは用意されていないコマンドを呼ぶ

# やったこと

AWS LambdaでPythonからgsコマンドを呼んでPDFを結合しました。

# 経緯
PythonのPyPDF2を使って結合しようとしましたが
PyPDF2はPDFのファイル構造がPDFのbasicなファイル構造（つまりheaderがあって、bodyがあって、Cross-reference Tableがあって、Trailerがあるみたいなもの）を前提としていて、Cross-reference TableがないとかTrailerがないPDFファイルを結合しようとするとエラーになってしまっていました。
gsコマンドならいけたので、LambdaでPythonからgsコマンドを呼ぶことにしました。

# 環境
AWS Lambda Python3.7

# 解決

Pythonからlsコマンドとかを叩くのは[subprocessモジュール](https://docs.python.org/ja/3/library/subprocess.html)を使ってできるので、lambdaの実行環境のデフォルトから実行できるコマンドなら以下のような感じで十分

```python
out = subprocess.run(['ls'], stdout=subprocess.PIPE)
print(out.stdout.decode())
```

これで済めば話は簡単なのですが、ghostscriptはlambda実行環境では用意されてない（[昔はあったみたい](https://kyokomi.hatenablog.com/entry/2016/11/03/160234)だけど今はなくなってるっぽいhttps://aws.amazon.com/jp/amazon-linux-ami/2018-03-packages/ ）のでバイナリを持ってきてパスを通す必要がある。

```sh
$ curl -L -# https://github.com/ArtifexSoftware/ghostpdldownloads/releases/download/gs950/ghostscript-9.50-linux-x86_64.tgz -O
```
でバイナリファイルを入手する

```sh
$ tar zxvf ghostscript-9.50-linux-x86_64.tgz
```

で解凍したghostscript-9.50-linux-x86_64の中にgs-950-linux-x86_64ってのがあるのでこれをgsに名前を変えてlambda関数と同レイヤーにbinディレクトリでも作ってその中に入れる。
そしてzipで圧縮して、lambdaへ反映させる。
lambda関数のgsを呼ぶ部分は以下のような感じ

```python
os.environ['PATH'] = os.environ['PATH'] + ':/var/task/binary'
cmd = [
    "gs",
    "-dBATCH",
    "-dNOPAUSE",
    "-q",
    "-sDEVICE=pdfwrite",
    "-sOutputFile=out.pdf",
    "a.pdf",
    "b.pdf"
]
out = subprocess.run(cmd, stdout=subprocess.PIPE)
print(out.stdout.decode())
```


# 雑談
https://www.slideshare.net/field-works/pdf-29001511
古いけどこのスライドが言うにはAdobe Readerは間違ったPDFに寛容らしく、間違ったPDFを生成するツールが世の中に氾濫しているってさ
やめてほしい
VS Code上で自分好みにPythonのコード整形・コードチェックをしたい

# 動機
デフォルトのpylintだと，思うようにコード整形されなくて不便だったので，細かな設定を自分好みに変更する方法をまとめます．

# pipのインストール
もしすでに導入済みなら不要です．

```
sudo apt install -y python-pip
```

# フォーマッタをインストール
flake8：コードチェック（エラー警告）
autopep8：コード整形

```
pip install flake8 autopep8
```

# setting.jsonの設定

VS Codeの設定から`setting.json`を開き，`{ }`内に下記を追記

```
{
   "python.linting.pylintEnabled": false,
   "python.linting.flake8Enabled": true,
   "python.formatting.autopep8Args": [
       "--max-line-length", "500",
       "--ignore", "E501",
   ],
   "python.linting.flake8Args": [
       "--ignore=E501,E266,E302",
   ]
}
```
#### "python.linting.pylintEnabled": false
pylintを無効
#### "python.linting.flake8Enabled": true
flake8を有効
#### "--max-line-length", "500"
autopep8の1行の文字数制限を500（実質無し）に変更
#### "--ignore", "E501"
autopep8の1行の文字数制限を解除
上のとどちらか一方でいい気がするが，とりあえず入れている
#### "--ignore=E501,E266,E302"
E501：flake8の1行の文字数制限を解除
E266：flake8の#が多すぎるときの警告を解除
E302：関数やクラスの間の2行以上の改行がない場合の警告を解除

# 実際に使ってみる
`Ctrl` + `Shift` + `I`で使えるはずです．もし使えなければ，先程pipでflake8などを導入したPythonのバージョンとVS CodeのPythonのバージョンが同じかどうか，確認してみてください．

# 参考
https://qiita.com/psychoroid/items/2c2acc06c900d2c0c8cb
https://qiita.com/ciloholic/items/9de9391f8457dc9bc60c
ログインshellの変更、そしてrbenvでインストールしたRubyをデフォルトにする方法

ゴールはrbenvでインストールしたRubyをデフォルトで開けること。
→その設定が書かれた.bash_profileというファイルを
　デフォルトで読み込んでもらえないというのが問題でした。

前回記事
https://qiita.com/hacchi_mom/items/2095e6ac7aecd03f131a

# .bash_profileについて

まず.bash_profileってなんなんだというところから調べると
【ログインの際に読み込まれる設定ファイル】とのこと。

そして重要なのは、その設定ファイルは
【ログインshellがbashのときに読み込まれるもの】
ということ！！！！

# ログインshell問題

ログインshellとはログイン時に起動するshellのことですが
そもそも私の起動時のログインshellはbashではなくzshだったのです。

Terminal起動時に毎回まず`bash`と一度打ってから
全ての作業を始めておりました…そういうもんだと思っていました。笑

初歩すぎること書いててすいません。。。
だから.bash_profileを読み込んでもらえなかったんですね。

# ログインshellの変更への道

まずは以下のコマンドでbashを使う指示をしてみる。

```
$ chsh -s /usr/local/bin/bash
chsh: /usr/local/bin/bash: non-standard shell
```
早速拒否されたので笑
まず変更できるshellの一覧を出してみる。

```
$ cat /etc/shells
# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.

/bin/bash
/bin/csh
/bin/dash
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh
```
先ほどのコマンドを有効にするためには、
この/etc/shellsというファイルに
`/usr/local/bin/bash`を追記しなければならないそうです。

そしてそのファイルに書く手段として検索して出てきたのが【vim】

正直初心者には操作性が難しすぎた…

私にはvim以外の書き方を検索する力がなく、
以下のリンクを参考にして奇跡的に書けました。
https://original-game.com/vim-mac2/#m_heading-1

※vimについてはまだ説明できる力がないので割愛します。。
　Finderでファイル名検索とかしても非表示ファイルだから出てこないですが、
　エディタで編集する方法も調べればありそうです！

そしてもう一度、変更できるshellの一覧

```
$ cat /etc/shells
# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.

/usr/local/bin/bash　　←　これが無事追加されました！
/bin/bash
/bin/csh
/bin/dash
/bin/ksh
/bin/sh
```
そしてもう一度、最初に打ったbashを使う指示のコマンド

```
$ chsh -s /usr/local/bin/bash
chsh: WARNING: shell '/usr/local/bin/bash' does not exist
chsh: no changes made
```

さっきとエラー文が変わった！

これを調べるとコマンドではなく
**ターミナル >環境設定 >一般** のなかに
**【開くシェル】**という設定がありました！

これをデフォルト　→　コマンドに設定を変更し、
以下の画像のように`/bin/bash`と打ち込みます。

<img width="668" alt="スクリーンショット 2020-03-04 23.56.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595810/9c1f8fd4-ea88-0ced-8af8-b16694f5ba69.png">

これで最後にもう一度最初のコマンドを

```
$ chsh -s /usr/local/bin/bash
Changing shell for local.
Password for local:
```
(↑ちなみに最初の２回は割愛しましたが
 このコマンド打つと毎回パスワードを求められます。)

そしてターミナルを再起動すると
ログインshellはbashになっていました！

そして私のゴールだったRubyは…

```
$ which ruby
/Users/local/.rbenv/shims/ruby
```

できました！
これでrbenvでインストールしたRubyがデフォルトになりました。

# 参考記事
https://wa3.i-3-i.info/word13650.html
https://teratail.com/questions/59821
https://www.task-notes.com/entry/20150117/1421482066
https://qiita.com/n_oshiumi/items/5ea418bed44fb81dd653
https://creepfablic.site/2019/10/13/bash-zsh/#index_id4

たくさんの記事に助けられました！
長々と読んでくださった方、ありがとうございました！
GatsbyとNetlifyで技術ブログを作成する

## Gatsbyとは
Reactベースの静的サイトジェネレータのこと
利点や仕組みなどについては

Reactベース静的サイトジェネレータGatsbyの真の力をお見せします
https://qiita.com/uehaj/items/1b7f0a86596353587466 
に記載されています。技術ブログを開始しようと考えるにあたりReactベースのわりと新しめの技術を使いかったため使用いたしました。

## Netlify
静的なサイトを超高速で提供できるWebサービスのこと
上記のGatsbyと組み合わせることで、GitHubにPUSHしたフロントエンド資材を一気に公開することができます。

## 手順(yarn)
 gatsby-cliをグローバルにインストールします

```
yarn global add gatsby-cli
```

cliを利用して、ブログを作成します。
デザインテンプレートは [Gatsby Starters](https://www.gatsbyjs.org/starters/?v=2) を利用します。

```
gatsby new <site-name> <Gatsby Starters>
```

コンソール上にも表示されるが、
以下のコマンドで、内容をローカル確認することができる。

```
cd <site-name>
gatsby develop
```

`http://localhost:8000/`

ファイルの構造は、利用したスターターによると思うので、readmeとかを覗いて調べる。

## GitHubへPUSH
GitHubにプッシュしたNetlifyへのデプロイという流れなので、
まずはGitHubにPUSHします。

```
git init
git add .
git commit -m "first commit"
```

```
git remote add origin <追加したいリポジトリURL>
git push -u origin master
```

## Netlifyでデプロイ
アカウント作成後、以下のNew site from Gitをクリックします。

<img width="142" alt="スクリーンショット 2020-03-04 19.20.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205283/af85642e-6cb7-b3a8-ab80-9e0a7be7cbff.png">

Create a new siteのページに移るので
GitHubを選択します。

<img width="738" alt="スクリーンショット 2020-03-04 19.20.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205283/abf75cac-6e0c-e129-3eaf-c24a9a4b4a10.png">

ownerやBranchを設定して
最後に下のDeploy Siteを選択するとサイトがデプロイされます。

<img width="719" alt="スクリーンショット 2020-03-04 19.21.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205283/5b7d7c57-ce6b-f007-7732-65773cd28b3d.png">
データの鮮度にフォーカスした時間表示形式

# メルカリやtwitterなどの時刻表示形式を実装する

## サマリ
タイムラインなど、実際の時刻より現在時刻との差分が重視される時間表示を行う

ポイントとして、ある程度の敷居値で単位を秒、分、時間、日で区切って切り上げた上で表示を行う

## コード

```dart

String timeDiffView(int timeDiff) {
  String buff4Return = "0秒前";

  if (timeDiff < 60 * 1000) {
    buff4Return = (timeDiff / 1000).floor().toString() + "秒前";
  } else if (timeDiff < 60 * 60 * 1000) {
    buff4Return = (timeDiff / 1000 / 60).floor().toString() + "分前";
  } else if (timeDiff < 24 * 60 * 60 * 1000) {
    buff4Return = (timeDiff / 1000 / 60 / 60).floor().toString() + "時間前";
  } else {
    buff4Return = (timeDiff / 1000 / 60 / 60 / 24).floor().toString() + "日前";
  }

  return buff4Return;
}
```
Rustの複合データ型

#タプル
タプルとは複数のデータを並べて保持する複合データ型です。
実物を見るのが一番わかりやすいです。

~~~rust
fn main() {
    let tpl = (2, 3.7, "文字列");

    println!("tplの0番目のデータ：{}", tpl.0);
    println!("tplの1番目のデータ：{}", tpl.1);
    println!("tplの2番目のデータ：{}", tpl.2);
}
~~~
実行結果

~~~
tplの0番目のデータ：2
tplの1番目のデータ：3.7
tplの2番目のデータ：文字列
~~~
複数のデータをカンマ区切りで並べ、カッコで囲うことで、タプルになります。
タプルから特定のデータを取り出すときには順番を指定しなければいけません。以下のように書きます。
`タプル` `.` `順番`
順番は0から始まる点に注意してください。

複数のデータを一つの変数で扱う方法として最も簡単です。

#構造体(struct)
構造体はタプルのように複数の値を並べて保持する複合データ型ですが、あらかじめ定義しないと使えません。
以下のように書きます。

~~~rust
// 構造体名：Point
struct Point {
    x: f32, // フィールド名：x , 型：f32
    y: f32, // フィールド名：y , 型：f32
}

fn main() {
    // Point型のデータを変数pに束縛する
    let p = Point { x: 5.9, y: 1.1 };

    // 構造体の各フィールドは名前を使って取り出すことができる
    println!("フィールド名：x、　値：{}", p.x);
    println!("フィールド名：y、　値：{}", p.y);
}
~~~
構造体の定義は以下のように書きます。
`struct` `構造体名` `{` `フィールド名:型` `}`
`フィールド名:型`はカンマで区切れば何個でも書けます。また、最後のフィールドの後にカンマがあっても問題ありません。
構造体を持つ変数からフィールドの値を取り出すときは以下のように書きます。
`構造体を持つ変数名` `.` `フィールド名`



[The Rust Programming Language](https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/structs.html)では推奨されていませんが、フィールドの名前を省略した構造体を定義することが出来ます。
そのような構造体をタプル構造体と呼び、以下のように書きます。

~~~rust
// 構造体名：Point
struct Point (
    f32, // 0番目のフィールド, 型：f32
    f32, // 1番目のフィールド, 型：f32
);

fn main() {
    // Point型のデータを変数pに束縛する
    let p = Point ( 5.9, 1.1 );

    // タプル構造体の各フィールドはタプルと同じく順番を指定する
    println!("pの0番目のデータ：{}", p.0);
    println!("pの1番目のデータ：{}", p.1);
}
~~~
波カッコ`{}`ではなく丸カッコ`()`で囲っていることと、定義の終わりにセミコロン`;`があることに注意してください。
使い方はタプルとほぼ一緒です。定義済みのタプルといったところでしょうか。

さらに、データを持たない構造体を定義することも出来ます。

~~~rust
struct some_struct;

let x = some_struct;
~~~
当然ながら、単体では使い道がありません。
しかし、「構造体の定義は必要だけどデータはいらない」という状況が存在するようです。


[目次](https://qiita.com/tonesuke/items/52e469f18e747c794376)
Makefileで環境変数が設定されているか確認する処理を書く

# 背景
Makefileのコマンド実行時に、環境変数が設定されていることをバリデートする処理を書きたいことがあったのでメモ。

# 方法

ifndef を使って処理を書く。

## ifndefとは

以下を参照
http://quruli.ivory.ne.jp/document/make_3.79.1/make-jp_6.html

> ifndef variable-name
変数variable-nameが空の値を持つ場合、text-if-trueが有効となり、 そうでない場合は、text-if-falseが有効となります。

変数が定義されていない場合は、trueになる。

## コード

今回は、PROJECT_ROOT_PATH という(環境)変数が設定されていることを保証したい。

```Makefile:Makefile

validate-env-vars:
    ifndef PROJECT_ROOT_PATH
      echo 'PROJECT_ROOT_PATH を環境変数に設定して下さい。'
      exit 1
    endif

do-hogehoge: validate-env-vars
    # 処理
```

これで、`do-hogehoge` を実行する前に環境変数をチェックできる。
画像をjsonでPOSTしてflaskで受け取る

# やりたいこと
画像をjsonとしてPOSTして、flaskで受け取りたい。
ただ、bytes型のデータをjsonの値とすることはできないためひと工夫が必要。

ローカルでサーバーを立てて試してみた。

# base64って？
前述の通り、バイナリデータをjsonの要素とすることができない。テキストデータならOK。

それならばバイナリデータをある規則で一旦テキストデータに変換して伝送して、受け取った先で元のバイナリデータに変換すればよい。

バイナリデータをテキストデータに変換する規則の一つがbase64である。


#こうした
結構丁寧に解説。

まずクライアント側。
データの変遷は以下の通り。

画像をPillowImageとして読み込む
⇒ bytesに変換する
⇒ base64でエンコードする(まだbytes)
⇒ bytesだったデータをstrに変換する
⇒ json.dumpsしてjsonにする
⇒ 無事jsonでPOSTできる

```python:client.py
import requests
from PIL import Image
import json
import base64
from io import BytesIO

img = Image.open("iruka.jpeg")

# PillowImageをbytesに変換してさらにbase64に変換
buffered = BytesIO()
img.save(buffered, format="JPEG")
img_byte = buffered.getvalue() # bytes
img_base64 = base64.b64encode(img_byte) # base64でエンコードされたbytes ※strではない

# まだbytesなのでjson.dumpsするためにstrに変換(jsonの要素はbytes型に対応していないため)
img_str = img_base64.decode('utf-8') # str

files = {
    "text":"hogehoge",
    "img":img_str
    }

r = requests.post("http://127.0.0.1:5000", json=json.dumps(files)) # jsonとしてサーバーにPOSTする

print(r.json())


>>>{'img_shape': [750, 500], 'text': 'hogehogefuga'}

```


次いで、サーバーサイド。
データの変遷は以下の通り。

jsonで受け取る
⇒ jsonからお目当てのデータ（base64でエンコードされたテキストデータ)を取り出す
⇒ base64でエンコードされたテキストデータをデコードしてbytesに変換する
⇒ Pillowで扱えるように_io.BytesIOに変換する
⇒ 無事に元のPillowImageを取得できる

```python:server.py
from flask import Flask, jsonify, request
from PIL import Image
import json
import base64
from io import BytesIO
import matplotlib.pyplot as plt

app = Flask(__name__)

@app.route("/", methods=["GET", "POST"])
def index():
    json_data = request.get_json() # POSTされたjsonを取得
    dict_data = json.loads(json_data) # jsonを辞書に変換

    img = dict_data["img"] # base64を取り出す # str
    img = base64.b64decode(img) # base64に変換された画像データを元のバイナリデータに変換 # bytes
    img = BytesIO(img) # _io.BytesIO pillowで扱えるように変換
    img = Image.open(img) 
    img_shape = img.size # 取得した画像で適当に処理
    
    text = dict_data["text"] + "fuga" #取得したテキストで適当に処理

    # 処理結果をクライアントに返す
    response = {
        "text":text,
        "img_shape":img_shape        
        }

    return jsonify(response)

if __name__ == "__main__":
    app.debug = True
    app.run()
```


サーバーのレスポンスからも正しく画像を処理されたことが確認できた。

ちなみに
base64.b64encode()は入力がbytesで出力もbytes。
base64.b64decode()は入力はbytesでもstrでもよいが、出力はbytes。

# 参考
[python - コマンド - POSTからbase64をデコードしてPILで使用する](https://code-examples.net/ja/q/18dce13)

[How to convert PIL Image.image object to base64 string? [duplicate]
](https://stackoverflow.com/questions/31826335/how-to-convert-pil-image-image-object-to-base64-string)
Auth0のNew Universal Loginでサインアップ画面を直接呼び出す

いつの間にかAuth0のNew Universal Loginでもアプリからサインアップ画面に直接遷移できるようになっていたので簡単にですが試してみました。

ClassicのUniversal Loginではできてたけど、Newのほうはログイン画面にしか遷移できず、ログイン画面下部のリンクからしかサインアップ画面に行けなかったはず

## 今まで通りのログイン画面への遷移

手っ取り早く試すためにブラウザ（Chrome）のアドレスバーに`/authorize`のエンドポイントとパラメーターを打ち込んでます。
（どの画面が表示されるかを確認したいだけなのでstateとnonceは省略しています。）

![スクリーンショット 2020-03-04 19.18.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/79a8c691-15f8-dd4f-2f62-81a5e3ee9def.png)

```
GET https://.../authorize?
  scope=openid&
  response_type=code&
  client_id=...&
  redirect_uri=https://.../callback&
  state=xxxx&
  nonce=xxxx
```

Enterを押すといつものログイン画面が表示されます。
アカウントが未登録の場合は画面下部のリンクから、サインアップ画面に遷移してアカウント登録をしないといけません。

![スクリーンショット 2020-03-04 19.19.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/04ccf64c-247f-5ade-5d0a-a1eb9076fb2a.png)

## サインアップ画面への直接遷移

Auth0のNew Universal Loginのドキュメントにサインアップ画面に直接遷移するためのパラメーターが載っていました。（これも以前は載っていなかったはず）

[New Universal Login Experience](https://auth0.com/docs/universal-login/new)

ここに載っている`screen_hint=signup`パラメーターをつけて`/authorize`にアクセスします。

![スクリーンショット 2020-03-04 19.17.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/0dec7609-fbb0-f0a8-2c7e-74e6034584e8.png)

```
GET https://.../authorize?
  scope=openid&
  response_type=code&
  client_id=...&
  redirect_uri=https://.../callback&
  state=xxxx&
  nonce=xxxx&
  screen_hint=signup
```

すると、サインアップ画面に直接行けます。

![スクリーンショット 2020-03-04 19.19.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/4037ce36-1fbf-5b1f-f979-3c03286e2537.png)

## 追加パラメーター

Auth0のドキュメントには`/authorize`の追加パラメーターごとの振る舞いが以下のように記載されています。

[New Universal Login Experience #signup](https://auth0.com/docs/universal-login/new#signup)

<img width="806" alt="スクリーンショット 2020-03-04 23.06.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490013/fcd45cf9-c1ed-606d-a856-defe0739b4d6.png">

パラメーターとセッションの有無で振る舞いが変わるようです。
`prompt`パラメーター自体は前からあるやつですね。[Silent Authentication](https://auth0.com/docs/api-auth/tutorials/silent-authentication)で`prompt=none`って設定して使っていました。

## まとめ

New Universal Loginでも`/authorize`にパラメーターを追加することで、直接サインアップ画面に遷移することが可能になりました。これでサインアップとログインの導線を分けることができるようになったのでUI設計の幅が広がると思います。

New Universal LoginはClassicと比べるとまだ[制限](https://auth0.com/docs/universal-login/new-experience-limitations)がありますが、この制限もどんどん解消されていっているので今後にさらに期待したいですね。