OmniAICreator/Qiita-1.07M huggingface dataset:
Babelマクロを書いてみる

`babel-plugin-macros`というものの存在を知って、実際にマクロを書いてみました。

## `babel-plugin-macros`とは

すでに[Qiitaへ記事を書いている方がいる](https://qiita.com/oedkty/items/65c2d29f9e34bed9ac17)ので軽く触れる程度にしますが、BabelでJavaScriptを変換する際に、`import`した名前を使った特定の場所だけ特別な変換を入れられるようにする、という仕組みです。

ふつうにBabelのプラグインにするのと比べて、以下のようなメリット・デメリットがあります。

* メリット
 * `import`した名前と紐づくので、不意にあちこちで変換が適用されてしまうことがない
 * `babel.config.js`に`babel-plugin-macros`を入れておけば、あとは`import`で適用できるので、特殊な変換を自作する場合にも、そのたびごとにBabelの設定を変更する必要がない
* デメリット
 * ASTレベルでの変換なので、JavaScriptの文法から外れた入出力を行うことはできない
 * `import`して使う必要があるので、全コードへ一律に適用したい変換には向かない
 * 依存関係の制御ができない（他のファイルや時間など、外部データに依存した変換を行う場合、外部データが変化してもキャッシュが残り続けてしまう）

## マクロはどのように作るのか

実際にマクロを書き始める前に、マクロ内部での処理はどのように進むのかをまとめておきます。

まず、Babel内部では、JavaScriptコードはASTという形式で管理されていて、ちょうどHTMLをDOMにしたのと同じように、各コード片（ノード）がJavaScriptのオブジェクトとなっていて、ツリーをたどったり、操作したりといったことができます。[AST Explorer](https://astexplorer.net/)のような、JavaScriptコードがどのようなASTになるかを調べられるツールもあります（ツールによって微妙にノードの種類などが違いますので、Babelの参考にする場合は`@babel/parser`を選んでおきましょう）。

`import { foo } from 'some.macro'`のようにマクロを読み込んだソースコードで`foo`を書くと、マクロコードには`foo`のノードが渡されます。そこからASTをたどって改変を行う、という流れです（ソースコード全体のASTにアクセスできますので、その必要があれば`foo`と離れた箇所に手を入れることも可能です）。

## 簡単なものを書いてみる

例として、`import test from 'test.macro'`のように読み込んで、`test('a')`とすれば、コンパイル後のコードでは`a`の文字コードである`97`になっている、というようなマクロを書くことにします。

### 基本的なルール

マクロとして動かすファイルは、以下のようなルールで作る必要があります。

* `.macro`あるいは`/macro`で終わる名前で参照できるようにする
* `require`と`module.exports`を使うCommon.jsで書く
* マクロ処理は同期的に実行する

ということで、基本の枠は以下のようになります。

```javascript
const { createMacro } = require('babel-plugin-macros');

module.exports = createMacro((params) => {});
```

### マクロ関数の引数

`createMacro`の引数となった関数には、以下のようなキーを含むオブジェクト1つが引数として渡されます。

* `babel`…Babelの各種ライブラリ。`babel.type.***`が適切な型のASTノードを作るのに必要となります。
* `references`…これ自体もオブジェクトとなっていて、キーはこのマクロを`import`した名前（デフォルトインポートなら`default`）、値はソースに出現したノードの配列です。
* `state`…Babelの処理状態を反映したオブジェクト。細かい位置まで正確なエラーを出したい場合は、`state`を利用することになります（詳細略）。
* `config`…設定オブジェクト（詳細略）

`references`に来る配列を`forEach`で回しつつ、適切なノードに置き換えていく、というのが基本的な流れとなります。

### 実装例

今回は`default import`なので、`references.default.forEach`でループを回していきます。そして、関数名から`parentPath`で1階層上がれば、それは`CallExpression`のはずなのでそれを確認してから、引数を回収して処理を加え、書き出しておきます。

```javascript
const { createMacro, MacroError } = require('babel-plugin-macros');

module.exports = createMacro((params) => {
  const {babel, references} = params;
  references.default.forEach(node => {
    const call = node.parentPath;
    if(call.type !== 'CallExpression') throw new MacroError('エラーメッセージ');
    const rawValue = call.node.arguments[0].value;
    call.replaceWith(babel.type.numericLiteral(rawValue.charCodeAt(0)));
  });
});
```

### エラー処理

上の例でも関数呼び出し以外で来たときはエラーを出していますが、積極的に詳細なエラーを出すことをおすすめします。具体的には、

* `import`した名前が適当でなかった場合
* 想定した形以外のノードとしてASTに現れた場合
* 関数形で、引数が想定したものでなかった場合

などが考えられます。特に、関数の引数がリテラルでなかった場合には、「あくまでコンパイル時専用の関数として、リテラル以外を投げた場合はエラーにする」方法や、逆に「リテラルのときはコンパイル時に置き換えるけど、そうでない式であれば、実行時処理のコードを書き出しておく」など、いくつかの方策が考えられます。

簡便には`MacroError`を投げる方法がありますが、`state`をたどって詳細な位置まで含めたエラーを出すことも可能です。

## 外部リンク

* [`babel-plugin-macros` Usage for macros authors](https://github.com/kentcdodds/babel-plugin-macros/blob/main/other/docs/author.md)
git clone したプロジェクトをサクッと JetBrain 製品で起動する方法

https://pleiades.io/help/idea/working-with-the-ide-features-from-command-line.html#toolbox

に従い、

```
git clone git@github.com:ほげ/ふげ.git
cd ふげ
```

で、

```
idea .
```

```
pycharm .
```

```
webstorm .
```

とか。
AWS SAM CLI で mediaをPOST bodyに加えてPOSTできるようにする

# AWS SAM CLI で mediaをPOST bodyに加えてPOSTできるようにする

初めてPython3.8 Lambdaを利用した時の備忘録。
multipart-form-dataを利用してmedia(png, jpeg etc...)をPOSTした時に 502 BadGatewayが帰ってきたた

## TLDR

template,yamlにBinaryMeidiaTypesを付け加えれば、multipart-form-dataで送信できる。


```template.yaml
Globals:
  Function:
    Timeout: 3
  Api:
    BinaryMediaTypes: ["*~1*"]
```

BinaryMediaTypesについては、/を~1に変更する必要があります。


[AWS::Serverless::Api Document](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-resource-api.html#sam-api-binarymediatypes)


[Sample repo](https://github.com/Ntakuya/sam-multiple-form-data-sample/tree/feature/permission-any-file-data)
【Ruby】略語メソッド

【コード】

```
S = "BookShelf"

def acronym (s)
puts s.delete("abdefghijklmnopqrstuvwsyz")
end
acronym(S)
```

【出力結果】

```
BS
```
【解説】
英単語の頭文字で略語を作るメソッドです。
変数Sに連結する２つの英単語を代入します。

acronymメソッドを定義し、実引数(S)に変数Sを代入し、仮引数(s)へと渡します。

.deleteメソッドで小文字のa~zまでを指定し、putsで出力すると、小文字が全て削除された状態、つまり頭文字だけで略語を作るメソッドの完成です。
ボカロPになったし、爆速ホームページを作る！

## はじめに
６年くらい前に買ったボーカロイド。
一回も歌わせることなくタンスの奥に眠らせてしまったね。
ごめんよ、GUMI。

あれから時が経ち、新年新しいことをはじめようという気持ちになり...
ついに今年、ボカロPになることが出来ました...
ミュージシャンになったからホームページ作らないとなぁ...

ということで、ボカロPとしてのホームページつくりました！
https://onemuneko.com/
![site-top.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203126/e2027ade-5cdb-0300-f76d-12cfd1dbda6d.png)


Gatsby.jsというフレームワークを使っているので、爆速サイトになっています。
もう少し最適化したら、スコア高くなるかもしれません...
![lighthouse-score.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203126/967396b4-d51f-b1d8-51e2-e4f20eb99192.png)

## なぜ爆速？
Gatsbyは、「爆速」で表示されるWebサイトが、「爆速」で作れます。

表示速度に関しては
・画像の最適化
・ビルドして静的なページを公開

制作スピードに関しては
・豊富なプラグイン

によって支えられていると思います。
プラグイン１つで、`画像の最適化`、`SEO`、`マークダウン`、etc...と様々な機能を実装することが可能です。
`gatsby-image`での画像最適化はすごく便利だと思いました...

あとは、Wordpressなどの動的なページ作成と違い、あらかじめビルドしてHTMLを作成しておく形式なので表示も早いです。

## 運用方法
1. テキストエディタを使ってブログ記事を書く
2. GithubへとPUSH 
3. Netlifyで自動的にビルド

という流れで運用しております。

ブログ記事の部分は、MDXを利用しています。
MDXとは、MarkownとJSXを合体させたものです。
例えば、[こちら](https://onemuneko.com/posts/start-music-site)の記事は以下のように記述しています。

```markdown
---
title: 音楽サイト始めました！
slug: start-music-site
image: ./images/daw.jpeg
date: 2021-01-28
author: おねむねこ
category: 音楽
readTime: 10
---

import { Link } from "gatsby"

## このサイトについて

音楽、DTM、ボーカロイドについて記事を書いていこうと思います。

趣味でボーカロイド曲を作り始めました。

今後もたくさん音楽について発信していけたらと思いますので、よろしくお願いします！

<Video src="https://www.youtube.com/embed/Cnn18oDCBwE" title="I(アイ)" />

<Link to="/posts" className="btn center-btn">
  all posts
</Link>
```

Markdownから変換されて生成されるHTMLタグは、`MDXProvider`を使って置き換えることが出来ます。
独自コンポーネントもMarkdownに渡せるので、柔軟にカスタマイズ可能です。

```jsx:gatsby-browser.js
import { Headings, Video } from "./src/components/Blog"
const components = {
  h2: Headings.myH2,
  Video,
  // ...
}
export const wrapRootElement = ({ element }) => {
  return (
    <MDXProvider components={components}>
      {element}
    </MDXProvider>
  )
}
```

私は、VSCodeを使って記事を書いているのですが、拡張機能でシンタックスハイライトをつけることが出来るので、ストレスなく執筆可能です！

ホスティングサービスはNetlifyを利用しています。
よっぽどサイトの規模が大きくならない限りは無料で運用出来るはずです。
無料で独自ドメインと紐づけて、SSL化も出来て、本当に素晴らしいサービスですね。
Wordpress運用で同じことをしたいのであれば、月額500~1000円くらいかかりそうですよね。

## 学習方法
今回サイト制作するにあたって以下のUdemy教材を利用しました。
[Gatsby Tutorial and Projects Course](https://www.udemy.com/course/gatsby-tutorial-and-projects-course/)
チュートリアル１つ + 実践的な3つのプロジェクトで構成されています。
今回紹介した、MDXを使ったブログ制作以外にも、`Contentful`、`Strapi`、`Airtable`を利用してコンテンツを管理する方法が紹介されています。

上記のUdemy教材にて学んで良かった点
・GraphQLを利用して、コンテンツを管理する方法のイメージがついた
・styled-componentsを利用して、コンポーネント単位でCSSを付ける便利さに気づけた
・実践的にサイト制作を学べた
・CSSの書き方が参考になった

普段、フロントエンドのコードを書く機会があまり無いので、参考になりました...！

## 最後に
以前、Wordpressで個人ブログを運用していたのですが、`拡張のしにくさ`、`エディタの使いにくさ`、`サーバー代がかかる`という点で運用を辞めてしましました。

GatsbyはWordpressよりも`拡張しやすい`と思いますし、`好きなテキストエディタでブログ書けます`し、`無料で運用出来ます`し、最高だと思います。
これは音楽活動が捗りそうです！

ちなみにですが、上記で紹介しましたUdemyは動画教材ですので、React触ったことある人も無い人も、Gatsbyの使い方がイメージしやすいのではないかなと思います。
皆さんも何か発信したくなった時は、Udemyで学んで、Gatsbyでサイトを作ることをおすすめします！

最後まで読んでいただき、ありがとうございました。
How to share your Cloud9 environment via Internet?

#Overeview
When you share your cloud9 environment with other developers or coworkers, you need to create a specific user and attach the appropriate policy to this user. You finish creating this user, you can download CSV file which is described URL, ID, password for access to your cloud9 environment.

## Step.1 Click the most top of right "share" link as follow:
![Screen Shot 2021-01-30 at 14.01.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367259/57451ca3-3640-7a42-1baf-3628d1e69532.png)

## Step.2 Click "Create user" link as follow:
![Screen Shot 2021-01-30 at 14.00.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367259/bb298a7f-62c3-b1a3-e691-7a00de6576f8.png)

## Step.3 Create User for access to your environment as follow:
![Screen Shot 2021-01-30 at 13.47.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367259/a8ca8122-02b1-5b3c-a197-dcb219c1ae6e.png)

## Step.4 Attach cloud9 user policy as follows:
![Screen Shot 2021-01-30 at 13.48.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367259/53f8f5dd-9efc-247e-4b35-132d9fc9da45.png)

## Step.5 Skip Tag setting as follows:
![Screen Shot 2021-01-30 at 13.49.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367259/6b893332-24ef-9782-6c3e-5838d67f7ffb.png)

## Step.6 Create User
![Screen Shot 2021-01-30 at 13.49.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367259/bea8d219-beb7-4915-c3e4-d16819f46758.png)

## Step.7 Share URL, ID, Password information to User via Email.
![Screen Shot 2021-01-30 at 13.58.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367259/b9836515-b076-7dce-756c-2e679696ebf9.png)

## Step.8 Invite user.
![Screen Shot 2021-01-30 at 13.55.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367259/66c23e56-6be4-0bf8-6867-f815a4cacb4f.png)
APサーバとは

## 勉強前イメージ

初めて聞いた

## 調査

### APサーバとは

AP とは アプリケーションの略で、アプリケーションを実行するためのサーバのことです。

### web3層構造システム

web動かすには基本的に下記のようなサーバを使用します

- webサーバ
- AP(アプリケーション)サーバ
- DBサーバ

![12021-01-30 APサーバとは - diagrams.net - Google Chrome 2021-01-30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/9252921a-1f8d-eabf-3423-9328bae20062.png)

1. クライアントからのリクエストを `webサーバ` が受け取る
2. webサーバからの処理リクエストを `APサーバ` が受け取る
3. APサーバでアプリケーションを実行
4. アプリケーションが `DBサーバ` を操作して、結果を処理に入れる
5. 結果を `webサーバ` にわたす
6. webサーバがクライアントにデータを表示

### webサーバとAPサーバの違い

- webサーバ : webのデータを表示させるサーバ
- APサーバ : java,ruby,php などを実行して処理を行う

料理で例えると、
料理を乗せるお皿 = webサーバ = データを表示させる
食材を調理する  = APサーバ = アプリケーションの処理を行う

![ryouriAPサーバとは - diagrams.net - Google Chrome 2021-01-30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/fbef3019-c0aa-d85b-6604-bf555f032121.png)


### よくwebサーバにアプリケーションが乗ってるけど・・・

webサーバの中でアプリケーションが処理されることもあります。
そのようなサーバを `webアプリケーションサーバ` と呼ばれます。

## 勉強後イメージ

webとアプリケーションって基本的にはくっついてんのかなー？ってけど、
たしかに役割的には別れてる・・・

## 参考

- [APサーバの役割とは？Webサーバとの違いをしっかり理解しよう！](https://kitsune.blog/ap-server-summary)
- [主要サーバ解説｜アプリケーションサーバ｜トラムシステム](https://www.tramsystem.jp/voice/voice-2431/)
- [Webアプリケーションサーバとは？メリット4つと特徴をご紹介！](https://www.fenet.jp/dotnet/column/environment/4573/)
自己紹介1　何をどの様にどういう目的で書きたいか

#はじめに

ブログというものは、以前少しやったことはあるが全く続かなかった。というより、始めようとしてブログを立ち上げたこと数回、
何をどのように書くかも明確でなく、記事はほぼ皆無で仕事で忙しい中（言い訳）で放置されていた。

今は、以前と比べ書きやすい環境が整ったことと、既に技術ブログを書いている方々の書き方を参考させていただきながらやる、下記項目に具体的な方針を決めることいったことでなんとか続けていけたらという思いでこの記事を書いています。

#目的

・技術力の向上、幅を広げる
仕事だとプロジェクトが変わったりしないと使用する技術は限られることが多いですね。
仕事とは別に勉強をすることで、技術の幅を広げたいと考えています。また、すでにやったことあることでも、深堀りしていけたらと
思います。

・勉強のモチベーション向上

・頭の整理、後で見返すため
・文章力の向上

#何を書きたいか？

興味があることと、関連キーワード

・デバイスドライバー開発
WDM(Windows Driver Model), Windows Driver Foundation

・組み込み開発
Raspberry Pi、H8マイコン、RTOS、オシロスコープ、電子回路

・機械学習
Deep Learning、画像認識、OpenCV、統計学、python

その他、以下もやりたいが上記よりは優先度は劣る。特にWindowsプログラミングはこれを書いている現在の仕事関連なので仕方なくやる側面もある。

・Windowsプログラミング
→.NET Framework,MFC,Win32 API

・Webプログラミング
→Ruby on Rails, PHP

・スマホアプリ開発
→iOS,Andoroid

#いつか書きたいか?

休日が中心になるかもしれません。理想は毎日ですが。。

#どのように書きたいか？

・学習したことのメモをとして自分の理解を整理して書く
学習内容の性質も以下のように分けれると思うが、それぞれやっていく中でスタイルを確立したい。

1. 知識
2. プログラミング
3. 単発の内容
4. 続けて勉強している内容


・やった作業内容をその都度メモ

#最後に

こうやって記事を書いてみると、つたない文章でも少しは頭が整理＆モチベーションが高まってきたように思う。
（気のせいかもしれないが)
また、初めての記事であるためまだまだ使いこなせていないが、ブログを楽しくかける日々が来ることを願い、ひとまずこの記事を終わる。

#参考記事
[技術ブログを書こう](slideshare.net/akira6592/happyblogging) 

[^1]:
[^2]:
Windows10にUbuntu20.04インストール

# あらかじめ対処しておく設定
## WslRegisterDistribution failed with error: 0xc03a001a
[WSL2 のインストール時に WslRegisterDistribution failed with error: 0xc03a001a で失敗するときの対処法](https://qiita.com/kuryus/items/27a7206c64eca7ba710b)

### WSL Issue（上の別の対応方法）
[WSL2: Virtual hard disk files must be uncompressed and unencrypted and must not be sparse ](https://github.com/microsoft/WSL/issues/4103#issuecomment-501511504)

以下コマンドが紹介されている。

```powershell
# Windowsファイル暗号化（EFS）をオフにする（
fsutil behavior set disableencryption 1
# Windowsファイルの圧縮を無効にする
fsutil behavior set disablecompression 1
```
# WSL インストール
https://docs.microsoft.com/ja-jp/windows/wsl/install-win10

# Micro Soft Storeより、Ubuntuを選択
バージョン表記のないものも20.04LTS（2021/01/30時点）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87538/b2517285-7915-7ed6-adaa-e02d3c7ece0a.png)

# Ubuntu起動
## apt-get update
```bash
sudo apt-get update
```
# wsl デフォルトをUbuntuにする
## Power Shell起動 
### 現在の設定
```console
wsl --list --verbose
```
*印が現在の設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87538/d4239c3f-3926-e0e9-d26a-cd4810bfb8d7.png)

### Ubuntuへ変更
```
wsl --set-default Ubuntu
```
*印がUbuntuになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87538/9f020a84-73f4-aa7b-e52a-29bce784e840.png)

これで、wslと入れるとUbuntuのコンソールになる。
LEDの制限抵抗、電流別の輝度


備忘録

表面実装LEDは定格輝度が眩しすぎる為、制限抵抗別の輝度をチェックした。

使用LED LTSN-B32JEGBB-LY
https://akizukidenshi.com/catalog/g/gI-12898/

電圧は13.5V
10kΩ、4,7k、2.7kΩ、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067139/bf0e3c31-2943-d3d2-102b-bf3b8857d37f.png)


2mA程度で十分な輝度。
正規表現を使った小技

見つけたら都度書いていく。

## 特定の文字数で区切りたい
力技でやるマンだとこんな風に書いてしまうが、

```ruby
text = 'abcdefghijklmn'
text.chars.each_slice(5).map(&:join)
# => ["abcde", "fghij", "klmn"]
```

正規表現だとこんなにスッキリ

```ruby
text = 'abcdefghijklmn'
text.scan(/.{1,5}/)
# => ["abcde", "fghij", "klmn"]
```

## 単語境界というメタ文字がある
単語境界 \b
非単語境界 \B

ある文字列の中から先頭が0で始まる単語が存在するか

```ruby
'10abcdes0fg'.match(/\b0\B/)
# => nil

'10abcdes 0fg'.match(/\b0\B/)
# => #<MatchData "0">
```

## 英数以外は空文字に置換
POSIXクラスというものがある

```ruby
'asx., .pd[;m1@346lewmoefsm'.gsub(/[^[:alnum:]]/, '')
# => "asxpdm1346lewmoefsm"
```
【MacOS】Homebrewを使わずに、goenvでGoの環境構築

# 概要
Homebrewでgoenvをインストールすると最新のGoが選択できなかったので、giuhubから直接goenvをインストールして最新のGoを使える環境を構築しました。
その手順を記載しています。

# 実行環境
- macOS Big Sur 11.1

# 手順
## 1. Homebrewからgoenvを削除

```
$ rm -rf `goenv root`
$ brew uninstall goenv
```

下記コマンドでgoenvが表示されないことが確認できたら完了。

```
$ brew list
```

## 2. goenvの再インストール

```
$ git clone https://github.com/syndbg/goenv.git ~/.goenv
```

`~/.bash_profile`にgoenvで使用する環境変数・パスを追加。

```
$ vim ~/.bash_profile
```

追加する設定は下記3つ。

```
export GOENV_ROOT="$HOME/.goenv"
export PATH="$GOENV_ROOT/bin:$PATH"
eval "$(goenv init -)"
```

変更を反映するために下記コマンドを実行。

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

## 3. Goのインストール
インストールできるgoのバージョンを確認。

```
$ goenv install -l
```

Goのインストール。(今回はバージョン1.15.6を選択)

```
$ goenv install 1.15.6
```

下記コマンドでインストール済みのGoのバージョン一覧を確認できる。

```
$ goenv versions
```

goで使用する環境変数・パスの追加。

```
$ vim ~/.bash_profile
```

追加する内容は下記2つ。

```
export PATH="$GOROOT/bin:$PATH"
export PATH="$PATH:$GOPATH/bin"
```

変更の反映。

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

下記コマンドで使用したいGoのバージョンが表示されれば完了。

```
$ goenv global 1.15.6
$ go version
```
position:stickyを理解する

###positionの役割とレパートリー

`position`はウィンドウのどの位置に配置するかというのを指定できるcssプロパティです。

positionには`static`、`relative`、`absolute`、`fixed`と4種類のレパートリーがあります。
つい最近まで`sticky`の存在を知らなかったのですがとても便利なので使用方法を書いていこうと思います。

###position:stickyとは

`sticky`はウィンドウに表示されたあとスクロールしていっても固定表示させることができます。
和訳するとねばねばとベタつく、粘着するというような意味で、
`sticky`を記載した部分だけウィンドウにくっつく(スクロールしてもウィンドウ上に固定表示される)ようなニュアンスです。

とても便利だけど動かなくなるケースもあります。
それを以下に説明します。

###制約① 子要素がこれのみだと動かない
`position:sticky`は兄弟要素の上でしか固定表示ができません。
兄弟要素がなく1つしか子要素を入れていない状態だと固定表示して上を通れる兄弟要素がないため、機能しません。


###制約② 親要素の中でしか機能しない
`position:sticky`を指定すると自動的に親要素をstickyの箱と定義します。
箱のなかでしか固定表示されないので、親要素以外の部分になるとフレームアウトします。

###制約を理解してつかいやすいメニューバーを作ろう
ウィンドウ上部に追従してくるメニューバーは操作のしやすさからオウンドメディアやブログなどによく見かけるようになりました。
他の方法で固定しても問題ないけど、stickyを知っているかどうかで作業効率が上がりそうだなと思ったので記事にしました。
自分メモ：ShellでSSHしまくった時のトラブル解消メモ

# はじめに

シェルスクリプトでSSHして、複数のクライアントにはいり、SQLiteにselect文を発行して、結果を取得する。ということをやった時にトラブったのでメモ書き。（超初心者向け）

# なぜシェルスクリプトか？

ネットにつながっていない環境でしたので、ライブラリを追加することはできませんでした。（というかできるならpythonあたりでやりたかった。）

# トラブル1

ループでSSHをで入ることを考えたが、一つ目に入ると、シェルが終了する。

## トラブル1原因

シェルでSSHすると、コンソールがSSHで入った先に移るため、SSHさきでexitすると、シェル自体が終了してしまっていた。

## トラブル1解決法

sshで起動するときに、-n オプションを入れ、コンソールを渡さないようする。
また、同一シェル内でSSHすると、解消しなかったため、メイン、サブ構成にして、サブ側でSSH入るようにした。

# トラブル2

SSHで入る際にパスワードを渡す方法。

## トラブル2原因

まあ、推し量って下さい・・・

## トラブル2解決法
[@wadahiro様の記事](https://qiita.com/wadahiro/items/977e4f820b4451a2e5e0)を参考にさせていただきました。

なお、chmodで実行権を与えてください。
直接起動しないとうまく動きません。

# トラブル3

初めてつなぐクライアントでフィンガープリントが表示される。
トラブル2解消後であったため、yes/noが入力できず、無限ループになる。

## トラブル3原因

・・・・そういうもんですよね。

## トラブル3解決法

SSHのオプションに -o ”StrictHostKeyChecking no" を入れる。

# トラブル4

SSHで入った後、SQLiteでselectする際、select付近にエラーがある旨のメッセージが出力される。

```
user@mac:~/documents $ ssh user@192.168.1.xxx sqlite3 db.sqlite3 "select * from test"
user@192.168.1.xxx's password:
Error: near "select": syntax error
```

## トラブル4原因

一度SSHで入ってからsqlite3 db.sqlite3 "select ・・・”すると、当然ですが成功します。

## トラブル4解決法

コマンド部分を''で囲む。
ssh user@192.168.1.xxx 'sqlite3 db.sqlite3 "select * from test"'

で解決。

というわけで自分メモ。
Ruby + RBS + steep で再帰型を定義する

# 目的

jsonのような、Hash型がHashを持っているような場合にどう記述するか？という問題です。

# 環境

```
$ ruby --version
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux]
$ rbs --version
rbs 1.0.3
$ steep --version
0.39.0
```

# 対象のコード

以下のような、入力次第ではいくらでも深く出来るような実装を考えます。`C.rec` の返値の型には何を指定すれば良いのでしょうか？

```rb
module C
  def self.rec
    str = gets
    return nil if str.nil?

    { val: str.chomp, next: rec }
  end
end

p C.rec
```

```
$ ruby lib/test.rb 
a
g
c
{:val=>"a", :next=>{:val=>"g", :next=>{:val=>"c", :next=>nil}}}
```

# 解決策

type エイリアスを使う。エイリアス自身を含む型も定義できるようです。

```
type jhash = Hash[Symbol, String | nil | jhash]

module C
  def self.rec: () -> jhash?
end
```

steep チェックも問題なく通ります。

```
$ steep check
```
【メモ】効率的なパラメータ探索方法（ブースティング）

* 製造業出身のデータサイエンティストがお送りする記事
* 今回はハイパーパラメータの探索で、無駄な事を実施していたことが分かったのでメモとして記事に残しておきます。

##はじめに
普段、機械学習モデルを構築する際、ハイパーパラメータの探索をしているかと思います。ちなみに、普段、私はグリッドサーチやベイズ最適化を使用しております。
その際、回帰問題や分類問題で勾配ブースティング木のモデルを使用する事が多いのですが、`n_estimator`（構築する決定木の数）のパラメータを対象に探索していて無駄をしていることが分かったので自分のメモとして残しておきます。

##なぜ、n_estimatorのパラメータ探索は無駄か
勾配ブースティング木において、`n_estimator`は構築する決定木の数を表しております。イメージは下記です。

![スクリーンショット 2021-01-30 11.50.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743453/99e99fea-9b0e-1698-6c3a-5125dacce878.png)

勾配ブースティング木は、弱分類器の予測値の誤差を新しく作った弱学習器がどんどん引き継いでいきながら誤差を小さくしていく方法です。
つまり、`n_estimator`のパラメータを100、500、1000とグリッドサーチで探索する際、決定木は合計で「100+500+1000=1600」構築することになります。
しかし、勾配ブースティング木の場合は、「n_estimator＝1000」の時に「n_estimator＝100」や「n_estimator＝500」のスコアも算出されているのでわざわざパラメータ探索をする必要がないということです。

##n_estimatorのパラメータ設定方法はどうするべきか
結論から言うと、`n_estimator`のパラメータを大きめに設定し、EarlyStoppingを活用して最適な決定木の数を決める事が良いのではないかと思います。

##EarlyStoppingとは
モデルの学習の収束を判定するための方法です。具体的なやり方としては、validation lossを監視し，train lossは減少し続けるのに対して，validation lossが改善されなくなった場合に学習を打ち切ることでオーバーフィッティングを抑制し、汎化性能が高いモデルを構築する事ができます。

![スクリーンショット 2021-01-30 12.55.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743453/7b1ffdaf-35ff-12ee-c8ea-0674b3b606ad.png)

（参考文献：Kaggleで勝つデータ分析の技術より引用）

##具体的な実装方法
今回、ライブラリーにEarlyStoppingのパラメータがあるLightGBMで実装してみます。

```php
# LightGBMライブラリ
import lightgbm as lgb

import pandas as pd
import numpy as np
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split

# スコア計算のためのライブラリ
from sklearn.metrics import r2_score
from sklearn.metrics import mean_squared_error

# ボストン住宅の価格のデータセットを読み込む
boston = load_boston()
X, y = boston.data, boston.target

# 訓練データとテストデータに分割する
X_train, X_test, y_train, y_test = train_test_split(X, y)

# LightGBM用のデータセットに加工
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test)

# ハイパーパラメータの設定
params = {'metric': 'rmse',
          'max_depth' : 9}

# モデルの学習
gbm = lgb.train(params,
                lgb_train,
                valid_sets=lgb_eval,
                num_boost_round=10000,
                early_stopping_rounds=20,
                verbose_eval=5)
```

![スクリーンショット 2021-01-30 12.36.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/743453/11141032-edbb-7c86-6268-5759e65451c2.png)

各パラメータについて説明します。
* params：ハイパーパラメータの設定
* lgb_train：訓練データ
* valid_sets：評価用データ
* num_boost_round：学習サイクル
* early_stopping_rounds：EarlyStoppingのパラメータ
* verbose_eval：学習過程を表示するサイクル

上記モデルでは、20サイクル分観察し、過学習しているような場合は学習を終わらせております。

一応、評価データの予測まで実施します。

```php
pred = gbm.predict(X_test)

# 評価
r2 = r2_score(y_test, pred)
rmse = np.sqrt(mean_squared_error(y_test, pred))

print("R2 : %.3f" % r2)
print("RMSE : %.3f" % rmse)

#R2 : 0.878
#RMSE : 2.784
```

##さいごに
最後まで読んで頂き、ありがとうございました。
訂正要望がありましたら、ご連絡頂けますと幸いです。
【Laravel初心者】Laravelのモデル作成とマイグレーションの流れ

Laravel初学者です。
オリジナルアプリを作成しているのでその過程を記事にしています。

理解が曖昧なところも多いため、ご指摘等ありましたらご連絡いただければ幸いです。

今回はLaravelでのモデル作成とマイグレーションまでの流れです。

## 環境

|                | Version     |
|:---------------|------------:|
| PHP            | 7.4.14      |
| Laravel        | 8.24.0      |
| mysql          | 8.0.23      |
| docker         | 20.10.2     |
| docker-compose | 1.27.4      |


## モデル作成

```PHP:ターミナル
php artisan make:model <モデル名> -m
```

上記のようにartisanコマンドでモデル作成します。
`-m`をつけることで一緒にマイグレーションファイルも生成されます。

今回私の場合はGameモデルを作りたかったので

```PHP:ターミナル
php artisan make:model Game -m
```

としました。

ターミナルには以下のように表示されれば成功です。

![スクリーンショット 2021-01-29 12.04.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1051152/9c798645-b318-78f2-db49-0da94333b6ca.png)


このコマンドにより
`app/Models`配下に`Game.php`のファイルと
`database/migrations`配下に`2021_01_××××_create_games_table.php`のマイグレーションファイルが作成されます。


## マイグレーションファイルの修正

`database/migrations`配下の`2021_01_××××_create_games_table.php`を開くと以下のようになっています。

```PHP:2021_01_××××_create_games_table.php
public function up()
    {
        Schema::create('games', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
```

初期の状態のカラムは`id`と`timestamps`のみです。
`timestamps`は`created_at`と`updated_at`のカラムになります。
`created_at`は作成日時の意味で`updated_at`は更新日時という意味です。


これを私の今回のプロジェクト用に編集します。

```PHP:2021_01_××××_create_games_table.php
public function up()
    {
        Schema::create('games', function (Blueprint $table) {
            $table->id();
            $table->bigInteger('user_id');
            $table->string('name');
            $table->text('describe');
            $table->integer('play_time');
            $table->integer('players_minimum');
            $table->integer('players_max');
            $table->timestamps();
        });
    }
```

`user_id`は`bigIntegerでbigint型のカラム`にしています。
ここがイマイチ理解できていないのですが、user_idを外部キーとして使うためです。
`name`は`string型`で文字列として登録します。
`describe`は`text型`として長い文章を入力するためのカラムです。
`play_time`、`players_minimum`、`players_max`は`integer型`で数字として扱います。


この状態で

```PHP:ターミナル
php artisan migrate
```

上記コマンドでマイグレーションします。

![スクリーンショット 2021-01-29 13.56.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1051152/28427537-aaca-9261-ed78-ed7f607fffd0.png)

上記のように出力されれば成功です。


## ロールバックについて

私はLaravelの前にRuby on Railsも勉強していますが、最初はマイグレーションとロールバックをする意味が分かりませんでした。

マイグレーションファイルにカラム型を記述し、それを反映させるのがマイグレーション。
Laravelの場合は

```PHP:ターミナル
php artisan migrate
```

のコマンドでマイグレーションできます。

そのマイグレーションをした後に、マイグレーションファイルを書き換えても反映されません。
カラムを変更したい場合はロールバックで一度差し戻す必要があります。

Laravelの場合は

```PHP:ターミナル
php artisan migrate:rollback
```

で一つ前のマイグレーションをロールバックできます。

その後マイグレーションファイルを変更し、再度`php artisan migrate`すればマイグレーションできます。

ただ、もしデータを登録してあるテーブルをロールバックすることで登録したデータは消えてしまうのでご注意ください。
CentOS on Raspberry Pi : 32bit版, 64bit版のパフォーマンス 

#Raspberry Pi 向けのCentOS 32bit版と64bit版の性能差

結果的には、64bit 版は32bit版の3倍程度の性能がでました。

以下、比較した内容・手順です。

昨年(2020)春先に、Raspberry Pi 4 4GB に CentOS 32bit 版をいれて利用していました。
その頃は以下の記事を参考にさせていただきました。

[Raspberry Pi 4 で CentOS 7 を動かす]
(https://soji256.hatenablog.jp/entry/2019/12/05/010449)

32bit版が動いているマシンの uname の結果は以下です。
初期状態のメモリー使用量も表示しておきます。

```
[root@mame-t06 ~]# uname -a
Linux mame-t06 5.4.72-v7l.1.el7 #1 SMP Wed Oct 21 17:20:56 UTC 2020 armv7l armv7l armv7l GNU/Linux
[root@mame-t06 ~]# free -h
              total        used        free      shared  buff/cache   available
Mem:           3.7G         60M        3.4G         24M        281M        3.6G
Swap:          487M          0B        487M
[root@mame-t06 ~]#

```

その後、昨年(2020)秋頃に、Raspberry Pi 向けの CentOS 64bit版イメージがダウンロードできるようになったようで、先日インストールしてみました。以下の記事を参考にさせていただきました。

[Raspberry Pi 4でCentOS 7ルートパーティションを拡張するには]
(https://denor.jp/raspberry-pi-4%E3%81%A7centos-7%E3%83%AB%E3%83%BC%E3%83%88%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E6%8B%A1%E5%BC%B5%E3%81%99%E3%82%8B%E3%81%AB%E3%81%AF)

```
[root@mame-t08 ~]# uname -a
Linux mame-t08 5.4.72-v8.1.el7 #1 SMP PREEMPT Wed Oct 21 17:35:36 UTC 2020 aarch64 aarch64 aarch64 GNU/Linux
[root@mame-t08 ~]# free -h
              total        used        free      shared  buff/cache   available
Mem:           3.7G         92M        3.3G         16M        356M        3.6G
Swap:          487M          0B        487M
[root@mame-t08 ~]#

```

ベンチマークは、以下の記事を参考にさせていただきました。
[ALL about Linux お手軽なベンチマークあれこれ]
(https://luna2-linux.blogspot.com/2015/05/blog-post.html)

32bit版での計測

```
[root@mame-t06 ~]# LANG=C
[root@mame-t06 ~]# time for ((i=0;++i<1000000;))
> do
> :
> done

real    0m39.503s
user    0m36.675s
sys     0m2.827s
[root@mame-t06 ~]#

```

64bit版での計測

```
[root@mame-t08 ~]# LANG=C
[root@mame-t08 ~]# time for ((i=0;++i<1000000;))
> do
> :
> done

real    0m13.408s
user    0m12.606s
sys     0m0.802s
[root@mame-t08 ~]#

```

100万回ループの処理時間が
32bit版は、39.503 秒　に対し　64bit版は、13.408 秒 と、性能は約3倍になっています。

参考に、x86_64 on でも計測してみました。
マシンは、富士通の [PRIMERGY TX1310 M1]
(https://www.pg-direct.jp/blog/?p=11866)
CPU : Intel(R) Celeron(R) CPU G1820 @ 2.70GHz
MEM : DDR3-1600 SDRAM 4GB

```
[root@aaa ~]# uname -a
Linux aaa 2.6.32-754.33.1.el6.x86_64 #1 SMP Tue Aug 25 15:29:40 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
[root@aaa ~]# LANG=C
[root@aaa ~]# time for ((i=0;++i<1000000;))
> do
> :
> done

real    0m4.130s
user    0m3.730s
sys     0m0.396s
[root@aaa ~]# 

```

富士通がラインナップしているPCサーバーの中では最弱な部類にはいる TX1310 M1 では、4 秒と Raspberry Pi と比較するとさらに約3倍になっています。CPU性能が影響していますね。

今回は、Raspberry Pi に 32bit版と64bit版のCentOSをいれて、パフォーマンスを比較してみました。結果は、限られた条件（処理内容）ですが、64bit版は 3倍 程度、速いというのがわかりました。
簡単なcatコマンド自作(低水準入出力)

#概要
簡単なcatコマンドを自作しました。オプションなしです。
コマンドライン引数からファイル名を受け取り、ファイルの中身を標準出力へ出力します。
低水準入出力(open,read,write,close)を使用しました。

ただし、低水準入出力は使い方によってはエラーが起こりやすいので、
特別な理由がない限りは標準ライブラリ(fopen,fread,fwrite,fclose)を使うことをお勧めします。

#ソースコード

```c:simple_cat.c
#include<stdio.h>		/* fprintf,perror   */
#include<stdlib.h>		/* exit             */
#include<unistd.h>		/* read,write,close */
#include<sys/types.h>   /* read             */
#include<fcntl.h>	    /* open             */
#include<sys/uio.h>		/* read             */

static void simple_cat(const char *path);
static void die(const char *str);

int main(int argc, char *argv[])
{
	int i;
	
	if(argc < 2){
		fprintf(stderr, "%s: file name not given\n",argv[0]);
		exit(1);
	}
	for(i = 1;i < argc; i++)
		simple_cat(argv[i]);
	exit(0);
}

#define BUFSIZE 4096

static void simple_cat(const char *path)
{
	int fd;
	unsigned char buf[BUFSIZE];
	ssize_t cc;

	if((fd = open(path,O_RDONLY)) == -1)
		die(path);
	for(;;){
		cc = read(fd, buf, sizeof buf);
		if(cc == -1)
			die(path);
		if(cc == 0)
			break;
		if(write(STDOUT_FILENO, buf, cc) == -1)
			die(path);
	}
	if(close(fd) == -1)
		die(path);
}

static void die(const char *str)
{
	perror(str);
	exit(1);
}
```
#実行

```bash
$ gcc simple_cat.c
$ ./a.out (ファイル1) (ファイル1)

$ (ファイル1の内容)
  (ファイル2の内容)
```
#解説

#### main()

```c
int main(int argc, char *argv[])
{
	int i;
	
	if(argc < 2){
		fprintf(stderr, "%s: file name not given\n",argv[0]);
		exit(1);
	}
	for(i = 1;i < argc; i++)
		simple_cat(argv[i]);
	exit(0);
}
```
最初のif文でコマンドライン引数あるか確認して、なければエラーメッセージを出力してexit()します。
ここで出てきたargv[0]にはこのプログラムの名前が入っています。
次にfor文で全てのコマンドライン引数を順番に繰り返しsimple_cat()にファイル名を渡します。
#### simple_cat()

```c
#define BUFSIZE 4096

static void simple_cat(const char *path)
{
	int fd;
	unsigned char buf[BUFSIZE];
	ssize_t cc;

	if((fd = open(path,O_RDONLY)) == -1)
		die(path);
	for(;;){
		cc = read(fd, buf, sizeof buf);
		if(cc == -1)
			die(path);
		if(cc == 0)
			break;
		if(write(STDOUT_FILENO, buf, cc) == -1)
			die(path);
	}
	if(close(fd) == -1)
		die(path);
}
```
最初にopen()でファイルを開きます。O_RDONLYは読み込み専用のオプションです。
戻り値はファイル記述子(FileDescriptor)という小さな非負整数です。
失敗した時は、-1が返されdie()を呼び出し処理を終了します。

次に無限ループのfor文の中でファイルの読み込み、書き込みを繰り返します。

read()はファイルにあるバイト列をプロセスのメモリ領域(バッファ)に読み込みます。
戻り値は実際に読み込んだバイト数です。
EOF(ファイルの終わり)に達すると0を返し、breakで無限ループを抜け出します。
失敗した時は、-1が返されdie()を呼び出し処理を終了します。

write()はバッファの内容をファイルに書き込みます。この場合は、関数の第一引数にSTDOUT_FILENOが設定されているので標準出力に書き込みます。
戻り値は実際に書き込んだバイト数です。
失敗した時は、-1が返されdie()を呼び出し処理を終了します。

最後にclose()でファイルを閉じます。
戻り値は成功すると0、失敗すると-1が返されます。

#### die()

```c
static void die(const char *str)
{
	perror(str);
	exit(1);
}
```

perror()はエラーメッセージをエラー出力に出力します。
exit()はプロセスを終了します。

#参考文献
「ふつうのLinuxプログラミング第２版Linuxの仕組みから学べるgccプログラミングの王道」,青木峰郎 著,SBクリエイティブ株式会社,2017
Seleniumで押せないボタンをどうにかして押したかった話

#初めに
Python×SeleniumでWebサイトいじっていた時に、どうしても押せなかったボタンがあり時間を浪費してしまったので
今後こんなことが無いように備忘用にメモ。

#やりたかったこと
とあるサイト(サイト名は伏せさせて頂きます)からCSVデータをダウンロードしたかったんです。

具体的には下図の「CSV export」を押したかったんです。

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513378/0c3f5df9-2668-5db7-8839-67e02a04c279.png#center)

手動でCSV exportを押す場合の手順は以下の通りとなります。

<ol>
<li>右上の雲マークをクリックするとXLSX export, CSV exportのリストが展開される</li>
<li>CSV exportをクリックする</li>
</ol>

で、問題はどこだったかというと、Seleniumで1.の雲マークがクリックできなかったんです。
正確にはクリックは出来たんですが、リストが展開されなかったんです。

ちなみに私はHTML周りはど素人なので、なんでリストが展開されないかとかは一切わかりません。

#解決策
どうしようか悩みながらサイトのソースを見ていたら、
雲マークをクリックする前後で以下の属性値が変わっていることがわかりました。

![ｸﾘｯｸ前後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513378/e73bfe60-10a7-68c9-f7f4-cc0bc757a0f6.png#center)

どうやらこのdiv要素のClassを"module-menu invisible"から"module-menu visible"に書き換えればリストを展開できそうです。

```python
script = "document.getElementsByClassName('module-menu invisible')[0].className = 'module-menu visible';"
driver.execute_script(script)
```

#結論
出来た。
[Rails]時々見かけるメソッド内の「? :」の組み合わせ

#概要
初学者にとって難しいのは知らないメソッド、記法が出てきたときだと思います。
筆者も教材をやっているときに出くわしそこでエラーがでるとひえーっとなってしまいます。

#メソッド内のに:と?が記載されていて
さて、今回でくわした記法は __?:__ です。下記のような感じです。

```hoge.rb
def hoge
  hoge = params[:hoge].present? ? params[:hoge] : ""
end
```

こんな感じですよね。
え、わたし:(コロン)てシンボルとかハッシュにしか使ったことないよ？これハッシュ関係ある?となりあたふた。
でもメソッド内のに:と?が記載されていて明らかに何か意味してるよね、と...。


#条件演算子
調べたところ、
「条件演算子（三項演算子とも）」だそうです。え、そうなの？笑
三項演算子知っているしそうなら解読すごく簡単じゃん！拍子抜けです。
というか条件分岐はif文でなんとかなると思ってました。

##使い方
式１? 式2 : 式3
「式1の結果次第で式２か３を返す」という意味なんですね！！
if文でいうなら
if 式1 then 式2 else 式3 end 
と同じです。if文だと行が増えるので可視性を高めるためにも使われているのかなと納得しています。
なので上記のコードをif文に落とし込むとこんな感じです。

```hoge.rb
def hoge
  if hoge.present?
    hoge = params[:hoge]
  else
    hoge = ""
  end  
end
```

間違いがあればご指摘いただけますと幸いです。以上！

#参考文献
https://www.javadrive.jp/ruby/if/index10.html
https://docs.ruby-lang.org/ja/latest/doc/symref.html

今回の出来事は調べるときどうググったらいいのかわからないパターンでした。
"rails :" "rails コロン" "rails : ?" とか調べましたが引っかかるのはハッシュ関連が多かったですね。
結局"rails コロン　メソッド"で検索かけらうまいこと見つかりました！
docker分からないメンバーにも簡単にdocker環境を使ってもらう方法

#はじめに

経験の浅い新人や、dockerに不慣れな人にも簡単にdocker環境を作る

#dockerコマンドを記載したbatファイルを準備
docker up

```boot.bat
@echo off
docker-compose up -d
pause
```
docker down

```down.bat
@echo off
docker-compose down
pause
```
docker restart

```restart.bat
@echo off
docker-compose restart
pause
```

docker refresh
(キャッシュクリア)

```refresh.bat
@echo off
docker-compose exec 【コンテナ名】 /bin/bash -c "cd /var/www/html/【プロジェクトディレクトリ】 && 【キャッシュクリアコマンド】"
pause
```

laravelのキャッシュクリアをする場合

```refresh.bat
@echo off
docker-compose exec 【コンテナ名】/bin/bash -c "cd /var/www/html/【開発ディレクトリ】 && php artisan config:cache"
pause
```

#まとめ

開発中に使用するコマンドが限られているので、
必要なコマンドのみbat化すると便利です。
pythonのopencvでアニメキャラクター画像に背景をつける方

### 初めに
---


皆さん、はじめまして、RavenLeeです。東京に住んでるIT技術者です。
今回は私Qiitaに初めて投稿です、よろしくお願いします。

次はOpenCVで二つの画像を合成する方法を紹介したいと思います。使用された方法は簡単なので、pythonの初心者でも理解安いです。

PS:日本語勉強中ですが、上手く伝えられない場合、コメントしてください。 ;)

---
### 事前準備

#### 1. 環境
* python環境
* opencv
* matplotlib

*python環境内パッケージをインストール:*
```pip install matplotlib```
```pip install opencv-python```

#### 2. 画像

使用したのは[ネット](http://www.pngmart.com/image/tag/anime-girl)でダウンロードした画像です。


キャラクター(png:透過画像)
![character.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1053652/530476e4-79b0-8d96-7695-9222c677641b.png)

背景(jpg)
![background.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1053652/1ccf840f-896e-dbfd-4de0-9f7868064442.jpeg)


#### 3 import　パッケージ
```
import cv2
import matplotlib.pyplot as plt
```

#### 4 画像入力

```
img_background = cv2.imread("background.jpg")
plt.imshow(cv2.cvtColor(img_background,cv2.COLOR_RGB2BGR))
print(img_background.shape)


img_character = cv2.imread("character.png", flags=-1)
img_character = cv2.resize(
     img_character,
    (
        int(img_background.shape[0]/img_character.shape[0] * img_character.shape[1]),
        int(img_background.shape[0]/img_character.shape[0] * img_character.shape[0])
    ),
     interpolation=cv2.INTER_AREA)

plt.imshow(cv2.cvtColor(img_character,cv2.COLOR_RGB2BGR))
print(img_character.shape)
```
注意点：png透過画像は4のチャンネルがあるので、ロードする時は**flags=-1**の引数が必要です。


### 画像処理

#### 透過画像のalphaチャンネルを抽出し、マスクを作ります
```
mask = cv2.cvtColor(img_character[:,:,3], cv2.COLOR_GRAY2BGR)
plt.imshow(mask)
print(mask.shape)
```
出力結果

![mask.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1053652/457b61b2-0bbc-4ef6-33b8-9185d8323003.jpeg)


#### マスクによる、背景の色要素を選択する

```
bias_x = 0.2　# 人物の位置調整

img_copy_mask = np.zeros(img_background.shape,dtype=np.uint8)

y_ = int(img_background.shape[0]/2) - int(img_character.shape[0]/2) 
x_ = int(img_background.shape[1]/2) - int(img_character.shape[1]/2) + int(bias_x * img_background.shape[1]/2)  

img_copy_mask[y_:y_+img_character.shape[0],x_:x_+img_character.shape[1]] = mask  # 人物画像のマスクを作る

```
人物のmask 出力結果
![adj_mask.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1053652/39ac870f-45fa-f1ef-747c-e1af94f5a30e.jpeg)


```
img_copy_mask_2 = np.zeros(img_background.shape,dtype=np.uint8)
img_copy_mask_2[y_:y_+img_character.shape[0],x_:x_+img_character.shape[1]] = img_character[:,:,:-1]

mask_inv = cv2.bitwise_not(img_copy_mask)　# 背景画像のマスクを作る

```
背景のmask 出力結果
![mask_inv.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1053652/09606750-df26-9db1-5ffa-9e6901f400cd.jpeg)

次は、二つの画像を合成する

```
res = cv2.bitwise_and(img_background,mask_inv)
res2 = cv2.bitwise_and(img_copy_mask_2,img_copy_mask)

res_final = cv2.bitwise_or(res,res2)
plt.imshow(cv2.cvtColor(res_final,cv2.COLOR_RGB2BGR))
cv2.imwrite("result.jpg",res_final)

```
### 最終結果

![result.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1053652/c25a13c1-325a-361e-6142-5c52168a1709.jpeg)

同じ機能を持つ画像処理ソフトウェア、例えばSAI、photoshopがありますが、でも、コードで画像処理のメリットは大量の画像が処理必要場合に便利ですね。
Goでベンチマークテスト実行してみる


#はじめに
Goでベンチマークテストを実行する方法を忘れないようにメモ感覚で書いておく。

#対象の関数

```Go
func makeSlice() {
  s := make([]int, 0, 10)
  for i:=0; i<10; i++ {
    s = append(s, i+1)
  }
}
```



#ベンチマーク関数の実装
ユニットテストと同じように、次のようなフォーマットの関数として、_test.goという名前のファイルに記述する。

```Go
package main

import (
  "testing"
)

func BenchmarkMakeSlice(b *testing.B) {
  for i:=0; i<b.N; i++ {
    makeSlice()
  }
}
```

ベンチマークをとりたい関数をb.N回繰り返し精度を上げている。
コードが実行されるとb.Nは必要に応じて変化する。

#ベンチマークテストの実行

ベンチマークテストケースを実行するには、コマンドgo testを実行する際にベンチフラグ（- bench）を指定する。以下ではドットを使うことで全てのベンチマークファイルを実行している。

```
$ go test -v -bench .
```

実行結果は以下の通り

```
=== RUN   TestMakeSlice
--- PASS: TestMakeSlice (0.00s)
goos: darwin
goarch: amd64
pkg: awesomeProject
BenchmarkMakeSlice
BenchmarkMakeSlice-4    85463809                13.5 ns/op
PASS
ok      awesomeProject 1.179s
```

ループは85463809回実行し、各ループは13.5ナノ秒(0.0135ミリ秒)かかった。

上では、別で用意している機能テストも実行されている。
機能テストは実行せずベンチマークだけを実行したい場合は、フラグ「- run」を指定すればよい。このフラグはどの機能テストを実行するか指定する。そのため、どの機能テストとも一致しない名前を指定すれば全てのテストが無視される。

```
$ go test - run x -bench .
```

```
goos: darwin
goarch: amd64
pkg: awesomeProject
BenchmarkMakeSlice-4    76079278                13.6 ns/op
PASS
ok      awesomeProject 1.057s
```
【Vue.js】オブジェクトを代入するときの注意点

参考
[Vueオブジェクトの値渡し](https://qiita.com/kami_tsukai/items/444ada23bdd0994b4675)
#オブジェクトを代入する

```html
    <body>
        <div id="app">

            <p>オブジェクト1</p>
            <p>name:{{obj1.name}}</p>
            <p>count:{{obj1.count}}</p>

            <button v-on:click="change1()">変更1</button>

            <p>オブジェクト2</p>
            <p>name:{{obj2.name}}</p>
            <p>count:{{obj2.count}}</p>

        </div>

        <script>
            new Vue({
                el: '#app',
                data: {
                    obj1: {name: 'obj1', count: 1},
                    obj2: {name: 'obj2', count: 1}

                },
                methods: {
                    change1(){
                        this.obj2 = this.obj1;
                        this.obj2.count++;
                    }
                }
            });
        </script>
    </body>
```

オブジェクト1とオブジェクト2を用意します。
ボタンを押すと`chabge1`関数が呼び出されます。
オブジェクト1をオブジェクト2に代入した後、オブジェクト2の`count`を1増やします。

(変更前)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912003/69bf2050-2003-f721-209b-4081485edba2.png" width=20%>
↓
(変更後)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912003/c46021d8-b346-32b6-9db9-281dd81c9aaa.png" width=20%>
オブジェクト2は、オブジェクト1と同じように`name`がobj1になり、`count`は1増えて2になりました。想定どおりです。
しかしオブジェクト1の`count`も2になってしまいました。
これは、オブジェクト1をオブジェクト2に参照渡しで代入したためです。
このため、オブジェクト2を変更すると、それに合わせてオブジェクト1まで変更されてしまいました。
#オブジェクトをコピーして代入する
```html
    <body>
        <div id="app">

            <p>オブジェクト1</p>
            <p>name:{{obj1.name}}</p>
            <p>count:{{obj1.count}}</p>

            <button v-on:click="change2()">変更2</button>

            <p>オブジェクト2</p>
            <p>name:{{obj2.name}}</p>
            <p>count:{{obj2.count}}</p>

        </div>

        <script>
            new Vue({
                el: '#app',
                data: {
                    obj1: {name: 'obj1', count: 1},
                    obj2: {name: 'obj2', count: 1}
                },
                methods: {
                    change2(){
                        this.obj2 = Vue.util.extend({}, this.obj1);
                        this.obj2.count++;
                    }
                }
            });
        </script>
    </body>
```

`this.obj2 = Vue.util.extend({}, this.obj1);`の部分で、オブジェクト1をコピーして代入しています。

(変更前)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912003/6e9e9a2e-e96c-530f-f31a-cf6401014e5e.png" width=20%>
↓
(変更後)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912003/10e93a1f-b6db-d448-430d-2e960a5dd42b.png" width=20%>
オブジェクト2の`count`だけが2になりました。
【Rails】deviseで２種類のログイン機能を実装するには


##はじめに
オリジナルアプリで患者さんと看護師さんでログインページを分けた実装を行いました。
その際の、備忘録です。

##実行環境
devise (4.7.3, 4.7.2, 4.7.1)
ruby 2.3.0p0
rails 5.1.4

##手順
１、devise.rbの記述を変更
２、分けたい数のmodel/controller/viewを生成
３、routingを設定

##１、devise.rbの記述を変更

```/config/initializers/devise.rb
243  # ==> Scopes configuration
244  # Turn scoped views on. Before rendering "sessions/new", it will first check for
245  # "users/sessions/new". It's turned off by default because it's slower if you
246  # are using only default views.
247  # config.scoped_views = false
```
247行のコメントアウトを外して、trueへ変更する。

```/config/initializers/devise.rb
247  config.scoped_views = true
```
デフォルトの「false」の場合、ログアウトすると全てのスコープでログアウトされてしまうため。

##２、分けたい数のmodel/controller/viewを生成

```
各modelの生成 （単数形で）
$ rails g devise patient
$ rails g devise nurse

各contlloerの生成 （複数形で）
$ rails g devise:controllers patients
$ rails g devise:controllers nurses

各viewの生成 （複数形で）
$ rails g devise:views patients
$ rails g devise:views nurses
```

##３、routingを設定
```/config/routes.rb
devise_for :patients, controllers: {
  sessions:      'patients/sessions',
  passwords:     'patients/passwords',
  registrations: 'patients/registrations'
}
devise_for :nurses, controllers: {
  sessions:      'nurses/sessions',
  passwords:     'nurses/passwords',
  registrations: 'nurses/registrations'
}
```

##最後に
単体と比較しても、違う工程は２つくらいと思います。
まとめてみると簡単でした。
ほかにも認証機能の項目をデフォルトのemailでない項目で実装もしています。
[【rails】devise認証をemailではなく、違う項目でログインする。](https://qiita.com/pagk/items/cc548a914e70fb4a92b2)

##参考記事
複数のdeviseを作成して、別々のログイン画面を作る際のファイル作成からルーティング設定
https://qiita.com/kinpin/items/067eff7d2fbe74d85c83
Railsでdeviseひとつで複数モデルを管理しよう
https://qiita.com/Yama-to/items/54ab4ce08e126ef7dade
Basic認証・Digest認証・Form認証をざっくり理解

##はじめに
ここでは具体的な実装やアーキテクチャの話ではなく、webサーバーの認証機能についてざっくりと理解するための情報をまとめています。

どの認証方法を選べばいいのか、へのヒントになれば幸いです。

#Webサーバーの認証機能
クライアントがWebサーバーにアクセスする際に、サーバー側がクライアントを特定・認可するための機能です。
セキュリティの観点から、認証機能はWebサーバーには欠かせない存在とも言えます。
この認証方法には大きく分けて3種類。
HTTPがサポートする<font color="Red">Basic認証</font>・<font color="Red">Digest認証</font>、設計者が自分でログイン用htmlページを用意する<font color="Red">Form認証</font>です。

##Basic認証

Webサーバーへのアクセスで使われるHTTPプロトコルでサポートされているユーザー認証方式の1つです。  

クライアントがWebサーバーにアクセスすると、ログイン用のダイアログがブラウザに表示され、ダイアログにあらかじめ登録していたUsernameとPasswordを利用者が入力する仕組みです。
入力したUsernameとPasswordをBase64はエンコードされて、サーバーに送信されます。
<font color="Red">特にハッシュ化も暗号化もしていないので、盗聴・デコードされてしまえば中身は丸見え。</font>
これがBasic認証の大きな特徴であり、大きな問題です。

しかし、この問題はSSL/TLSプロトコル (ここではhttps)で通信をすれば暗号化されるので解消されます。 
この記事ではSSL/TLSの暗号化については割愛します。  

Basic認証は比較的実装が容易で、ほとんどのWebサーバー及びブラウザで対応しています。
よく聞くApacheやNginxのようなWebサーバーソフトウェアを用いて、設定することができます。

Basic認証ではログアウト機能はありません。
というのも、Basic認証ではブラウザが認証状態を握っているため、認証状態はブラウザを終了するまで残ります。

簡易的であればログアウト機能っぽいものも作れそうです。
本格的にログインログアウト機能やアカウントによるアクセス権限を付けたければ、Form認証が必要となります。

##Digest認証

Digest認証もBasic認証同様にHTTPプロトコルでサポートされているユーザー認証方式の1つです。
<font color="Red">唯一の違いは、UsernameとPasswordをハッシュ化してサーバーに送信することです。</font>
主にSSL/TLSでの通信ができない環境で使われる方式だと考えられます。

クライアントがサーバーにアクセスした際に、サーバーはクライアントにログイン画面と共にnonceとMD5のようなハッシュ化に使う関数を送ります。
クライアントは、ハッシュ化パスワード = MD5(nonce+Password)をサーバー側に送り返し、サーバー側にもともと登録してあるハッシュ化パスワードと比較することでユーザー認証が可能になります。

ここで、ハッシュ化パスワードごと盗聴し、そのデータをそのまま用いてログインするリプレイ攻撃への対策としてnonceが登場します。
nonce(number used once)とは使い捨てのランダム値であり、これに有効期限を定めることによって上記の攻撃を防ぐことができます。

ハッシュ化とはMD5やSHAなどのハッシュ関数を用いて、いかなる長さのデータであっても固定長の擬似乱数データに置き換えることを意味します。
ハッシュ化では同じ入力値に対して、必ず同じ出力になります。
異なる入力値に対しては、必ず異なる出力とは限りませんが、それがこのハッシュ関数の精度にかかっています。

暗号化との違いは、復元できるかどうか、です。
その値を元に戻す必要があるかどうかで暗号化との使い分けされます。

例えば、1つのユースケースとしてサーバー側にあらかじめパスワードを登録してログインする際に、サーバー側はハッシュ化されたパスワードさえ知っていれば、送られてくるハッシュ化後のパスワードと比較して認証することができます。
つまり復元する必要がないので、暗号化ではなくハッシュ化を利用します。
もとより、セキュリティの観点から生のパスワードや復元可能な状態のパスワードをサーバー側に保存すること自体がNGです。

##Form認証

Form認証とは自分で実装したhtmlのログインフォーム画面からUsenamseとPasswordを送信する認証方式です。
平文でのやりとりのため、SSL/TLSでの暗号化通信上で使われるのが一般的です。

一から自分で作るため、実装の難易度は上がりますが、その分自由度が高くなります。
上の2つと異なり、セッションという概念が登場し、ログイン状態がサーバー側で管理されます。
このセッションが有効な期間のみ、サーバーへのアクセスが可能になります。
そのため、<font color="Red">Basic・Digest認証ではできないログイン/ログアウトという機能が使えるようになります。</font>

具体的には、認証不要なログインページにUsernameとPasswordを入力・送信してサーバー側に登録されている情報と比較することで認証します。
認証情報を保存するために、セッションIDを発行し、レスポンスクッキーにセッションIDを付与します。
その後のページアクセスにはセッションIDの情報を含めてアクセスされ、サーバー側でセッションIDとセッションデータを照合し、アクセス許可を出します。

このよう実装の自由度も高いため、アカウントによって機能やページを制限することが可能になります。
管理者しか入れないページと一般アカウントも入れるページを区別したい時にはこのForm認証が必要になります。

##最後にまとめたい

のですが、メモなので特にまとめはないです。
追加の重要な情報やご指摘あればください。
ハッシュ化や暗号化方法については奥が深そうなので時間があればまた別に書きたいところです。
djangoで静的ファイルを表示するための備忘録

#django内の静的ファイル設定
個人的に静的ファイルの適用で詰まりました。
自分でも設定内容を忘れないために備忘録として残します。

以下のページが分かりやすかったので参考にしました。
https://qiita.com/saira/items/a1c565c4a2eace268a07

#djangoでcssが適用された設定

settings.pyの中身は以下のように設定した。

```
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

STATIC_URL = '/static/'

STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]

STATIC_ROOT = '/staticfiles'
```

#staticファイルを収集するディレクトリ

以下のコマンドによって、どこのディレクトリの静的ファイルをSTATIC_ROOTに集約するようになっているか確認しました。

```
python manage.py findstatic .
```

結果は以下の通り。

```
  /usr/local/lib/python3.6/site-packages/django/contrib/admin/static
  /code/[アプリ名]/static
```

以下を実行。

```
python manage.py collectedstatic
```

#Webブラウザでcssが適用されているか確認
以下を実行。

```
python manage.py runserver
```

色を変えただけですが、cssが適用されました。
これを応用して色々な静的ファイルを適用していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764778/edd0520a-5c62-88d5-79a6-51f45a681f52.png)
セッション管理に JWT を検討する際に参考になった意見

Rails では認証時に JWT を返す [knock](https://github.com/nsarno/knock) という gem があります。
この gem の Issue では様々な開発者から JWT に関する質問があり、それに対する著者の返答が勉強になるため、紹介します。


## サーバーサイドからユーザーを強制的に「ログアウト」させるにはどうすればいいですか？

> 悪いニュース: JWTでは、これをすぐに実行する方法がありません。
これは、トークンベースの認証がステートレスであるという事実に起因しています。つまり、セッションなどとは異なり、サーバーサイドで状態(「ユーザーがログインしているか」など)を管理しないということです。
ステートレスAPIを持つことのシンプルさとスケーラビリティの面でのメリットは大きく、強制ログアウトのような機能を犠牲にする価値があると私は考えています。
トークンのブラックリストを使ってこのような機能を再実装しようとするのは良いアイデアとは言えません。
代替案としては、トークンの有効期限を短くして、頻繁にローテーションすることが考えられます。

>> Q. トークンをデータベースに保存するのはどうでしょうか？

>> これは良い考えではありません。リクエストのたびに追加のデータベースクエリが必要になります。また、セッションを管理しなくて済むというメリットも大きく減ります。トークンを使う最大の利点の一つは、トークンをサーバーに保存しなくても、トークンが有効かどうかを判断できることです。

https://github.com/nsarno/knock/issues/15

## これは refresh token に対応しているのでしょうか？

> いいえ、Knock はリフレッシュトークンをサポートしていません。私はその必要性を感じたことはありませんし、寿命の長いリフレッシュトークンを追加することは、セキュリティ上の問題を追加し、JWTを使う上でのシンプルさを損なうと考えています。

>> Q. トークンの有効期限が1日で切れることは知っていますが(デフォルトでは)、それはユーザーが再度ログインしなければならないということですか?

>> トークンの有効期限が切れた後、ユーザーは再度ログインする必要があります。
リフレッシュトークンのようなものを実装したい理由は理解しています。しかし、この種の問題に対処する方法は他にもあると思います。例えば、スライディングセッションを考えてみましょう。
スライディングセッションでは、ユーザーが認証済みのアクションを行うたびに、新しい短命のトークンを送信することになります。ユーザーがアクティブである限り、そのユーザーは認証された状態を維持します。ユーザーが有効期限切れのトークンを送信した場合は、しばらくアクティブでない状態が続いていることを意味します。



https://github.com/nsarno/knock/issues/65

あえて refresh token に対応していないと。著者の主張もわかりますが、例えば toC 向けのアプリで2日後にアプリを開いたら再度ログインを求められることになります。 knock を採用する上で UX とセキュリティのどちらを取るかは検討すべき箇所になります。

## ぼやき

仮に knock を使わずに JWT の access token と refresh token を用意する方法を取ったとして、refresh token が漏れた場合に refresh token を無効化することができない。なので、knock のように token の有効期限を短くして、頻繁にローテーションしようとなると思われる。

その場合 JWT token の有効期限は甘く見積もっても1日~1週間ほどだろうか。
UX のためにもっと長く認証期間を保持したいとなると、JWT は検討から外れてきそう。

(refresh token だけブラックリスト運用を検討してみても良いかも知れない。)

### 合わせて読みたい

[JWTでセッション管理してはいけない](https://qiita.com/hakaicode/items/1d504a728156cf54b3f8)
【Github】メインブランチの最新情報を作業用ブランチに反映させる方法

##実行したいこと
**ローカル環境内で作業中のブランチに、リモート環境内のメインブランチ(開発段階で主軸として積極的にマージを行っているブランチ： master/developなど）の最新状態を反映させる。**

##`git merge origin/"メインブランチ名"`で作業用ブランチに最新状態を反映

**以下はメインブランチを`develop`, 作業用ブランチを`feature1`として説明***

①作業中のブランチに移動

```terminal:terminal
$ git checkout feature1
```

②作業途中の物をリモート環境にpushしておく（ない場合はそのまま③へ）

```terminal:terminal
$ git add *
$ git commit -m "任意のコメント"
$ git push origin feature1
```

③メインブランチの内容を作業用ブランチに反映させる

```terminal:terminal
$ git merge origin/develop
```

上記の手順によりリモート環境にあるメインブランチの最新情報を作業中のブランチに取り込むことができる。

**記載内容に間違いがあった場合はご指摘頂けると嬉しいです。
ご連絡お待ちしております。**
【Ruby】文字列を数値へ変換するメソッド

.to_iメソッド　整数値へ変換します

.to_fメソッド　小数点値へ変換します。

変数.map(&:to_i)　変数に入っている値を整数値へ変換します

変数.map(&:to_f)　変数に入っている値を小数値へ変換します
【Ruby】多角形の内角の和を求めるメソッド

【コード】

```
D = 3

def polygon(d)
  n = 180 * (d - 2)
  puts n
end

polygon(D)

```

【解説】

多角形の内角の和を求めるメソッドです。

変数Dに多角形の角数を代入します。

polygonメソッドを定義し、実引数(D)に角数を代入し、仮引数(d)に渡します。

メソッド内で内角の和を求める計算、180 * (角数(d) - 2)を実行し、nへ代入し、putsで出力します。

Dの値を変えることで、様々な多角形の内角の和を求めることができます。

また、D = gets.to_i　とすることで、標準入力から値を取得することもできます。
過去バージョンのPostgreSQLを使うためにDockerを活用する

PostgreSQLのサーバ側と、手元のクライアント側でバージョンの不整合があり
dumpしたデータの取り込みに問題が生じたために、サーバ・クライントでバージョンを合わせたいと考えた。

Macを使っている場合はbrewで過去バージョンを入れることも検討されるが
brewの過去をチェックアウトするような方法[^1]が必要なようで面倒に感じた。

今回は、Dockerを使用して環境構築を行うことにした。

```
FROM ubuntu:20.04
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
RUN apt-get install wget ca-certificates gnupg2 -y
RUN sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt focal-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
RUN cat /etc/apt/sources.list.d/pgdg.list
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
RUN apt-get update
RUN apt install postgresql-client-11 -y
RUN pg_basebackup -V
```

`ENV DEBIAN_FRONTEND noninteractive` はtzdataインストールで止まるときの対策[^2]です。

他の大部分の記述は apt-get にない postgresql-client-11 を使えるようにしています。
https://wiki.postgresql.org/wiki/Apt にコードの記載があります。

```
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
```

という記載がありますが lsb_release が動かないこともあり
20.04では実際に上手くこのままでは動作しませんでした。

```
Ubuntu 16.04 (xenial), 18.04 (bionic), 20.04 (focal), 20.10 (groovy, amd64 only)
```

20.04は先程のURLは記載の通り、focalであることが分かっていますので

```
RUN sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt focal-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
```

と書き換えました。
またsudoがRUNでは動かないようでしたのでsudoを消して記述を行いました。

[^1]: [Homebrewで過去バージョンのパッケージをインストールする方法 - SIS Lab](https://www.meganii.com/blog/2019/12/03/how-to-install-an-older-version-of-a-homebrew-package/)
[^2]: [Docker Ubuntu18.04でtzdataをinstallするときにtimezoneの選択をしないでinstallする](https://qiita.com/yagince/items/deba267f789604643bab)