OmniAICreator/Qiita-1.07M huggingface dataset:
【PHP】独習PHP 復習

この記事は以下の書籍を参考に執筆しました。

- [独習PHP 第3版](https://www.amazon.co.jp/dp/B01FH3KVNU/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)

#大文字小文字を区別しない
```php5
PRINT 'hello';
pRint 'hello';
```
>出典：[独習PHP 第3版](https://www.amazon.co.jp/dp/B01FH3KVNU/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)

この2つは問題なく動作する。

そういえば区別しないんだっけ、という感じ。
#演算子(オペレータ)と被演算子(オペランド)

```
       ┌────┬───┬─被演算子(オペランド)
       $x = 5 + 4
          └───┴演算子(オペレータ)
```
>出典：[独習PHP 第3版](https://www.amazon.co.jp/dp/B01FH3KVNU/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)

#値による代入と参照による代入
##変数の正体
値を格納するのはメモリの役割
メモリには場所を示すアドレスが振られている。
そのアドレスをいちいち入力するのはタイプミスの元となるから人がわかりやすいように名前をつけておく─────────これが変数の正体
##値による代入
`=`を使うのは基本的に値による代入
これはメモリ上の値を別のメモリにコピーすること
##参照による代入
メモリ上のアドレスそのものを引き渡す代入
元の変数と代入した変数は同じアドレスを見ている。
どちらかを変更したら、もう片方にも影響する。
参照による代入は`=&`を使う
複合代入演算子`&=`と混同しないように

>####&=
左辺と右辺をビット論理積した結果を左辺に代入

```php5
$x=1;
$y= &$x;参照コピー
```

#参考文献

- [独習PHP 第3版](https://www.amazon.co.jp/dp/B01FH3KVNU/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)
PyCharm+Djangoでの開発環境の書き分け方の一例

# TL;DR
PyCharmでは`Run/Debug Configurations`の設定と、Virtualenvの`activate`スクリプトの両方に開発環境のラベル（`dev/stg/prd`）を書くと楽。

# 背景
開発環境は通常マシンごとに

```shell
# 開発環境ラベル
export DEPLOY_ENV=<dev/stg/prd>
```
のように開発環境を記述する環境変数を書き分けると便利だ。だが、PyCharmを用いて開発するときには、楽をしようとするとちょっとややこしい。


# 課題
PyCharmで開発環境ラベルが必要になる状況は、以下2つのパターンがある。

```
1. 画面から`Run/Debug`する場合
2. PyCharmのターミナルで`python manage.py ...`を実行する場合（migration実行時など） 
```

#1. `Run/Debug`
この場合の環境変数設定は、別に難しくない。`Run/Debug Configurations`から環境変数を追加すれば可能だ。

ただし、`Configurationsの数 × 開発ラベルの種類`の数だけ設定しなくてはいけないのでちょっと面倒ではある。本当はProjectレベルの環境変数があったら便利なのかもしれないが、ここは良い解決策がないので地道に行おう。

# 2. `python manage.py ...`
Djangoで開発する際に、`python manage.py makemigrations`など、`Run/Debug`以外の実行環境でpythonを実行することは多い。しかし、この際の環境変数には（当然ながら）先に設定した`Run/Debug`の開発ラベル反映されていない。

そこで、別の環境変数設定方法が必要になる。しかし、いくつかアイデアを検討したが、あまりぱっとしたものが思うかばない。

#### 検討1：ターミナルからexportしたり、`.bash_profile`などで`export`する
➔ 1つのマシンで複数のProjectや開発環境がある場合があり、却下。

#### 検討2：`manage.py`系の実行を`Run/Debug Configuration`に設定する
➔ インタラクティブシェルを必要とすることが多く、そのような場合に立ち上がらないケースがあったのでこれも諦めた。


PyCharm側で良さげな機能があればよいのだが、これも残念ながら見つからない。

そこで、ちょっと荒業だが、virtualenvの`bin/activate`の最後で直接exportするように運用ルールを設定した。具体的には以下のとおり。

```shell
vi ~/.virtualenvs/<Virtualenv環境名>/bin/activate
# 最後にexport DEPLOY_ENV=<dev/stg/prd>を記述
```

こうすることで、`activate`と同時に開発環境ラベルが設定できて楽になる。virtualenv環境ごとに環境変数が設定できるので、マシンの中でも競合は起きない。

なお、このように書き分けるために、当然Virtualenv環境は`<Project名称>_<開発環境ラベル>`（例：`project_dev`）のように書き分ける必要があるのでそこは注意。


# まとめ
PyCharmにおける開発環境ラベルは、

- `Run/Debug Configurations`で、それぞれの開発環境ごとにラベルをきちんと書く

- また、`python manage.py...`でも反映させるために、`virtualenv`の`activate`の最後でもマシンごとに環境変数を`export`する


と良い。
java7環境でもStringJoinerを使ってカンマ区切りで文字列を連結する方法

# はじめに

私は現在、とあるSI企業に勤めていて、現在参画しているのがjavaの保守案件です。
保守開発中のシステムのバージョンが古く、残念ながらjava7を使用しています。

StringBuilderを使用して文字列連結（csv的な文字を生成するとき等）をする時に、**java8以上だったらStringJoinerで簡単にできるのに……**と思うことがたまにあります。
↓こういうの。

```java
    for (String hoge : 何かのリスト) {
      if (sb.length() != 0) {
        sb.append(",");
      }
      sb.append("連結したい文字列");
    }
```

java7以下のプロジェクトに参画中で同じような悩みを抱えているSIerの人は私以外にもいるのではと思い、**<font color="red">StringJoinerと同じ振る舞いをする自作クラスを作成し、java7環境下で使えるようにライブラリとして公開</font>**しました。

何でも良いので**自作のライブラリを一般公開する**という行為をしてみてかっただけです。

# ということでjava7用にStringJoinerを自作しました

StringJoinerという名前のクラスを自作し、ライブラリとして公開しました。
実態としてはStringBuilderのラッパークラスです。
内部でStringBuilderを持っていて、delimiter、prefix、suffixをよしなに連結するだけのクラスです。
ソースはこちら↓
https://github.com/lovelyswallow/swallowJava8/blob/master/src/main/java/com/lovelyswallow/likejava8/util/StringJoiner.java

# java7でStringJoinerを使うための準備

下記URLにアクセスしてjarを直接ダウンロードするなり、gradle等でjarの取得をするだけです。
java7では`java.util.StringJoiner`が使えないので、
代わりに`com.lovelyswallow.likejava8.util.StringJoiner`をimportすれば、java標準のStringJoinerと同じ使い方ができます。
https://mvnrepository.com/artifact/io.github.lovelyswallow/swallowJava8

1.0はミスって**<font color="red">java12でコンパイル</font>**をしてしまっているので、ターゲットとしている肝心のjava7環境下では動作しません（笑）
使用する際は1.1以上をご利用ください。
1.1と1.1.1は中身が同じです。1.1.1は**マイナーバージョンを上げる**という行為をしたくて空リリースをしただけです。

# StringJoinerの使い方

java標準のStringJoinerと同じメソッドを用意しているので、全く同じ使い方ができます。
java標準と違うのはコンストラクタの種類だけです。
今更説明する必要もないかもしれませんが、一応、使い方の例を書きました。

```java
import com.lovelyswallow.likejava8.util.StringJoiner;

final class Test {
  public static void main(String[] args) {
    StringJoiner sj1 = new StringJoiner(",");
    sj1.add("hoge");
    sj1.add("fuga");
    sj1.add("piyo");
    System.out.println(sj1.toString()); // => hoge,fuga,piyo

    StringJoiner sj2 = new StringJoiner(",", "prefix", "suffix");
    sj2.add("hoge");
    sj2.add("fuga");
    sj2.add("piyo");
    System.out.println(sj2.toString()); // => prefixhoge,fuga,piyosuffix

    // 実態はStringBuilderのラッパークラスなので、コンストラクタでcapacityの指定をできます。
    StringJoiner sj3 = new StringJoiner(",", 100);
    StringJoiner sj4 = new StringJoiner(",", "prefix", "suffix", 100);
  }
}
```


# 最後に

最初に言った通り、以下の人に向けたライブラリとなっているので、現在ではあまり使い道はありません。

* SIerの人
* java7の現場に参画中
* プロジェクトの都合でjavaのバージョンアップができない
* java8で生まれたAPIを使いたい

同じ悩みを抱えているSIerの方は私以外にもいると信じて今回このライブラリを作りました。

今後はStringJoiner以外にもjava7環境向けに何かjava8っぽいクラスを作成したいと考えています。
とは言え、Streamっぽい振る舞いをする何か作ったところでラムダ式が使えないと微妙かもしれないけど。。。

がんばろう日本 :poop:
レンタルサーバーでSETENVを設定しPHPで利用する【作成中】

レンタルサーバー上の.htaccess に<IfModule mime_module>が記述されている。意味は下記参照。
[MIMEタイプの追加(AddType)](https://www.adminweb.jp/apache/docroot/index4.html)
SETENVとは直接関連しないのでこのあとにSETENVを記述する

記述のしかたは下記を参考に実施する
[PHP .htaccess内に追加(SetEnv)した環境変数で開発環境／本番環境など動作環境を切り替える方法](https://analyzegear.co.jp/blog/464)

あんまり関係なさそうだが一応参考記事
[Apache の環境変数](https://key2.jp/manual/ja/env.html)
【メモ】MAC 公開鍵・秘密鍵作成

# 環境
- MAC

# 作成
## 1. 「/.ssh」ディレクトリに移動
```
cd ~/.ssh
```

## 2. 公開鍵・秘密鍵作成
```
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/home-folder/.ssh/id_rsa):Enter
Enter passphrase (empty for no passphrase):<パスワード>
Enter same passphrase again:<パスワード>
```
ZealでMDNのdocset(HTML, JS, CSS)がContent rendering errorで表示できない


[Content rendering error in MDN docsets · Issue #1155 · zealdocs/zeal · GitHub](https://github.com/zealdocs/zeal/issues/1155)にあるように、MDNは新しいJSの機能を使っているせいで、Docsetがこわれてしまうらしい。ちなみにMDNはReactを使っているらしい。

[このコメント](https://github.com/zealdocs/zeal/issues/1155#issuecomment-552956765)にあるように`$HOME/.local/share/Zeal/Zeal/docsets`以下にある全ての`react-main.*.js`を消去することで正常に表示できるようになる。*修正後はzealの再起動が必要。*

このIssueを読んでいくとわかるのだがこの問題はmasterではすでにfixされている。しかしリリースはされていないため自分でbuildするか、上記のtipsで一時的に使えるようにしてあげる必要がある。

Documentが更新されるたびに実行する必要がありzsh、bashなら下記のfunctionを.bashrc, .zshrcなりに登録しておくのがよい。

```bash
zeal-docs-fix() {
    pushd "$HOME/.local/share/Zeal/Zeal/docsets" >/dev/null || return
    find . -iname 'react-main*.js' -exec rm '{}' \;
    popd >/dev/null || exit
}
```
[Elixir]Qiitaの自分の記事をエクスポートする

# はじめに
- [「Qiita夏祭り2020 オンライン」ライブ配信](https://connpass.com/event/180539/) に参加しました
- パネルディスカッションの中で「あると便利だよね〜」と話にでていたエクスポートの件を[Elixir](https://elixir-lang.org/)を使って書いてみました
- 最大10,000件までエクスポートできます(たぶんね)
    - ~~自分の記事がそんなに多くないので本当に動くのかどうかは未確認 :sweat_smile:~~
    - **2020/12/20現在、私の記事は140件ほどあるので試してみたところ、ページネーションのところがちゃんと動くことを確認しました**
- [Elixir](https://elixir-lang.org/)は**1.11.2-otp-23**を使っています

# 0. インストールとプロジェクトの作成
- まずは[Elixir](https://elixir-lang.org/)をインストールしましょう
    - 手前味噌ですが、[インストール](https://qiita.com/torifukukaiou/items/d04d0273749c41eb50af#0-%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB) などを参考にしてください
- インストールができましたら以下のコマンドでプロジェクトを作ります

```
$ mix new qiita_export
$ cd qiita_export
```

# 1. 依存関係の解決

```elixir:mix.exs
  defp deps do
    [
      {:httpoison, "~> 1.6"}, # add
      {:jason, "~> 1.2"} # add
    ]
  end
```

```
$ mix deps.get
```

# 2. ソースコードを書く

```elixir:lib/qiita/api.ex
defmodule Qiita.Api do
  @token "secret"
  @per_page 100
  @headers [Authorization: "Bearer #{@token}", Accept: "Application/json; Charset=utf-8"]

  def items_count(id \\ "torifukukaiou") do
    HTTPoison.get("https://qiita.com/api/v2/users/#{id}")
    |> decode_response()
    |> Jason.decode!()
    |> Map.get("items_count")
  end

  def items_by_page(page) do
    "https://qiita.com/api/v2/authenticated_user/items?page=#{page}&per_page=#{@per_page}"
    |> HTTPoison.get(@headers)
    |> decode_response()
    |> Jason.decode!()
  end

  def max_page(items_count) when rem(items_count, @per_page) == 0 do
    div(items_count, @per_page) |> min(100)
  end

  def max_page(items_count) do
    (div(items_count, @per_page) + 1) |> min(100)
  end

  defp decode_response({:ok, %HTTPoison.Response{body: body, status_code: 200}}) do
    body
  end

  defp decode_response(_), do: raise("")
end
```

- tokenはご自身でログイン後、 https://qiita.com/settings/applications から取得してください

```elixir:qiita_export.ex
defmodule QiitaExport do
  @user_id "torifukukaiou"
  def run do
    max_page = Qiita.Api.items_count(@user_id) |> Qiita.Api.max_page()

    1..max_page
    |> Enum.each(fn page ->
      Qiita.Api.items_by_page(page)
      |> Enum.each(fn %{"body" => body, "title" => title} ->
        File.write!(filename(title), body)
      end)
    end)
  end

  defp filename(title) do
    title
    |> String.replace("/", "_") # ファイル名に使えない文字があったらここで調整
    |> Kernel.<>(".md")
  end
end
```

- (`.md`でいいんだよね? :thinking: )


# 3. 実行 :rocket::rocket::rocket:

```elixir
$ iex -S mix

iex> QiitaExport.run
:ok
``` 

# Wrapping Up
- [「Qiita夏祭り2020 オンライン」ライブ配信](https://connpass.com/event/180539/)は、いろいろな話を聞けておもしろかったです
- 正直に申しますと、私が書いた[【毎日自動更新】QiitaのElixir LGTMランキング！](https://qiita.com/torifukukaiou/items/1edb3e961acf002478fd)で、[QiitaオリジナルTシャツ](https://qiita.com/official-events/0e872f8893283d4fd212#qiita%E8%B3%9E)をGet!できるのではないかとワクワク期待をして参加しました :sweat_smile:
- 受賞はできませんでしたが、また次もあるようですのでそれまで精進を続けていきたいとおもっております
    - 記事を投稿できたので良しとします！　ヨシ
- **Enjoy!!! :rocket::rocket::rocket:**
サポートの切れたCentOSバージョンでyumを使う

サポートの切れたバージョンのOSを使う機会は皆無だが、検証などでどうしても必要になったときのために。

そのままyumコマンドを実行しても、リンク切れとなり使えない。
`/etc/yum.repos.d/CentOS-Vault.repo` に古いアーカイブバージョンのリポジトリの設定があるので、これを利用する。

```sh
yum update \
--disablerepo=base,updates,extras \
--enablerepo=C5.10-base,C5.10-updates,C5.10-extras
```
Windows Virtual Desktop Consent Page でエラー AADSTS650052 が出る

いやあ、ハマりましたよ。
Windows Virtual Desktop の最初の手順では、Windows Virtual Desktop Consent Page での有効化がありますよね。
これです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593361/016ed1cf-1e5d-e913-e985-06a530625227.png)
ここで、以下の様なエラーが出る場合があります。

```
AADSTS650052 Message The app needs access to a service(\"{name}\") that your organization
\"{organization}\" has not subscribed to or enabled. Contact your IT Admin to review the 
configuration of your service subscriptions.650052 Message The app needs access to a service
(\"{name}\") that your organization \"{organization}\" has not subscribed to or enabled. 
Contact your IT Admin to review the configuration of your service subscriptions.
```

これには、Windows Server アプリに対する同意が必要です。手順は簡単 [これ](https://login.microsoftonline.com/common/adminconsent?client_id=5a0aa725-4958-4b0c-80a9-34562e23f3b7&redirect_uri=https%3A%2F%2Frdweb.wvd.microsoft.com%2FRDWeb%2FConsentCallback) をクリックして「同意」するだけ。

この先は、スムーズにすすみました。

参考ページ
[テナントとホスト プールの作成](https://docs.microsoft.com/ja-jp/azure/virtual-desktop/virtual-desktop-fall-2019/troubleshoot-set-up-issues-2019)
[チュートリアル:Windows Virtual Desktop でテナントを作成する](https://docs.microsoft.com/ja-jp/azure/virtual-desktop/virtual-desktop-fall-2019/tenant-setup-azure-active-directory#grant-permissions-to-windows-virtual-desktop)
Dotfiles をエレガントにマネージメントする

![jitakutaiki_stayhome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486518/cf01aee2-54aa-db91-b63c-79c18d5e3423.png)

なんか横文字かぶれみたいな標題ですが（笑）
知っている方には今更な話でしょうけども、 **コピペにもシンボリックリンクにも他ツールにも依存もせずに** `.vimrc` `.bashrc` 等を github/gist 等でバージョン管理する術の紹介です

https://news.ycombinator.com/item?id=11070797
> I use:
>     git init --bare \$HOME/.myconf
>     alias config='/usr/bin/git --git-dir=\$HOME/.myconf/ --work-tree=\$HOME'
>     config config status.showUntrackedFiles no
> where my ~/.myconf directory is a git bare repository. Then any file within the home folder can be versioned with normal commands like:
>     config status
>     config add .vimrc
>     config commit -m "Add vimrc"
>     config add .config/redshift.conf
>     config commit -m "Add redshift config"
>     config push
> And so one…
> **No extra tooling, no symlinks, files are tracked on a version control system** , you can use different branches for different computers, you can replicate you configuration easily on new installation.

Dotfiles を github 等のリモートリポジトリでバージョン管理していくのはよい発想ではありますが、ローカルで肝心の読み込みをさせるために `$HOME` に配置する必要があります。
通常だと [作業ディレクトリ](https://stackoverflow.com/questions/39128500/working-tree-vs-working-directory#:~:text=A%20directory%20is%20a%20singular,all%20sub%20directories%20and%20files.) （`$HOME` ではない任意のどこか）にあるソースをいちいち `$HOME` に持っていくとか、同期したりしなければならないことになろうかと思います。
そのためにあっちからこっちにコピペしたりリンクを張ったりするわけですが、それがバカバカしくなってくるのですよ。

それならばと、スクリプトを書けば自動化出来るということもあるかもしれません。
けれどもよくよく考えてみれば、そんなつまらないコピペやリンクの生成スクリプトなんか書きたくないというのがエンジニア心というもの。
かといって追加のなんらかのツールとかに依存するのもイヤ
どうにかして管理負荷を最小化しながらも都合は良くできないのか?

そこで [bare](https://stackoverflow.com/questions/37992400/what-is-a-bare-repository-and-why-would-i-need-one) を活用した前掲のソリューションというわけです
[こちら](https://wiki.archlinux.org/index.php/Dotfiles#Tracking_dotfiles_directly_with_Git) でも紹介されてますね。やはり元ネタは冒頭のリンク先のようです
記号にword-bleak: break-all;が効かない件

テキストがコンテンツ領域から溢れる際、
表示範囲に合わせて改行するか、単語の切れ目で改行するかなど、テキストの改行の仕方について指定できるプロパティ`word-bleak`。

`word-break: break-all;`はテキストのどこでも（単語の途中でも）改行する指定ですが………

## してない。
<img width="588" alt="スクリーンショット 2020-07-22 19.23.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619638/dbfadaa0-ea37-e224-aef2-4df1ea5c10ae.png">
特定の半角記号では`word-break: break-all;`が効いてくれない様です。
（感嘆符やコロン、スラッシュなど。Chromeで確認）


## word-break: break-word; で解決
`word-break: break-word;`の指定により半角記号も折り返しが適用されます。

しかしIEとEdgeが非対応。
IEはともかくEdgeも対応したいところです…
良い対応方法があったら続報出します。
Redshiftでのクラスター構築

# はじめに

- ここではAWSのサービスの一つであるRedshiftにおけるクラスター作成について、備忘録も兼ねて簡単にまとめる。
- Redshiftについての基本的な知識や、AWSのネットワーク（VPCやセキュリティグループなど）に関してはここでは説明しない。
- 以下は、Windowsからの接続で検証を行った。

# 作成手順

- 以下の情報を入力後に「作成」を押す。

## 基本情報

- クラスタ名、ノードの種類、ノード数、マスターユーザー情報、DB名などを入力。
- ノード数は1つでも作成でき、リーダーノードがワーカーノードを兼任するようになる。  
 （その場合のノードロールはSharedとなるみたい）

## ネットワーク情報

### VPCの選択

- Redshiftのクラスターを作成するVPCを選択する。
- VPCがグレーで選択できない場合は、立てる予定のリージョンにそもそもVPCが存在しないか、サブネットグループが存在しないかが原因。
- サブネットグループの作成方法はAWS公式のYouTubeを参照　→　[YouTube](https://www.youtube.com/watch?v=eJ4cmQFw_mc)
        
### セキュリティグループの設定

- セキュリティグループに関しては、どういったアーキテクチャーにするかによるので、次項で説明。


## その他

- 外部公開設定もでき、公開にした場合は外部アドレスが付与されていた。
- 今回は外部公開する予定はなく、同一のVPC間の中で行う予定なので非公開とした。


# セキュリティグループについて

- 基本的には独立し、セキュリティの観点から直接アクセスできないようにし、踏み台経由のSSH接続とした。

- そのためセキュリティグループによる通信の許可設定を行い、以下の構成とした。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264860/cf499542-9202-c2e0-b1b9-8f3462f6b6cf.png" width=60%>


# 接続方法

- ローカル環境よりつなぐ場合は、以下のコマンドでSSHポートフォワーディングを行い、psqleditなどのクライアントツールで接続する。


```
ssh -N -L {ローカルの任意のポート}:{Redshiftのエンドポイント}:{Redshiftのポート} -i {キーペアファイルのパス} -p 22 ec2-user@{踏み台サーバーの外部IPアドレス}
```
多分これが一番早いと思います~個人的最速開発環境~

## 結論
- Heroku × Ruby on Rails × jQuery × Tailwind CSS + 既存のSaaS
- 仕様自体を上記の技術に寄せる。

## 強み
### Heroku
- インフラの構築→デプロイの設定が数分で終わる
- サーバーのことをほとんど意識しなくてよくなる
- アドオンがそこそこ豊富(パフォーマンス監視、バグ管理、etc)
- Heroku Flowが手軽かつ強力

[Heroku Flowをざっくりまとめる - Qiita](https://qiita.com/d0ne1s/items/f80b41cff373584f6c7c)

### Ruby on Rails
- 設定より規約(デフォルトの設定でいい感じになることが多い)
- Active Recordが強力
- 情報が多い(遭遇したエラーは大体誰かがすでに遭遇してる)
- gem(ライブラリ)が豊富

### jQuery
Railsをモノリスで使う場合、VueやReact、Angularはしんどい。
Stimulusは情報が少なく、ライブラリも少ない。
極めればStimulusが一番かもしれないが、今の自分には厳しい。
消去法でjQuery

### Tailwind CSS
- クラス名を決める必要がない
- クラス名の衝突がなくなり、BEMやCSS設計から開放される
- デザインの修正により不要になったCSSが残ってしまうことがない
- どの要素にどんなスタイルが当たっているかがすぐにわかる
- カラーコードやフォントサイズ、ブレイクポイント等の統一性を保ちやすい
- ネット上に転がっているサンプルコードを気軽に取り入れやすい(他の人が書いたコードでもカスタマイズが楽)
- スタイルの修正のたびにapp/assets/stylesheets/任意のフォルダ/任意のファイルを開く必要がなくなる

[Tailwind on Rails - Qiita](https://qiita.com/d0ne1s/items/5a2cf50167afd988e9f7)

### 既存のSaaS
メール: SendGrid
決済: Stripe
認証: Auth0
etc...

外部サービスを活用して楽に実装できる場合は積極的に活用します。

## 弱み+それをカバーする方法
### パフォーマンス(スピード)が不安
- CDNを使う
- クエリ最適化
- パーシャルを使いすぎないように注意する
- HTMLをgzip圧縮
- 画像を圧縮
- ブラウザにキャッシュさせる期間を伸ばす
- 重い処理があればバックグラウンドに回す
- (金を積む)

この辺りの基本的なことをしっかりやって、Herokuの月25ドル以上のプランを使っていれば普通はそこそこのスピードがでます。

[実はHerokuで充分なのでは問題｜こんぴゅ｜note](https://note.com/konpyu/n/nb424a9a2ea8a)

### Tailwind CSS単体では実装が難しいデザインがある
Tailwind CSSで実装しやすいデザインにする。

### クロスプラットフォーム対応する場合、APIが使いまわせない
webアプリだけ作る場合は問題なし。

### jQueryを使うとコードがカオスになる
- なるべくjQueryを使わなくて良いような仕様にする
- `js.erb`や`remote: true`を使っていい感じに書く
- 開発に関わる人数を極力減らす

ここが一番の肝だと思います。

[JavaScript SPA 周りの議論で出た私なりの答え - ボクココ](https://www.bokukoko.info/entry/2016/05/21/JavaScript_SPA_%E5%91%A8%E3%82%8A%E3%81%AE%E8%AD%B0%E8%AB%96%E3%81%A7%E5%87%BA%E3%81%9F%E7%A7%81%E3%81%AA%E3%82%8A%E3%81%AE%E7%AD%94%E3%81%88)
[Railsで remote: true と js.erbを使って簡単にAjax(非同期通信)を実装しよう！(いいね機能のデモ付) - Qiita](https://qiita.com/__tambo__/items/45211df065e0c037d032)

## 機能するケース、しないケース
### 特に機能するケース
- 個人開発
- MVP作り
- 小~中規模の受託開発

エンジニアが仕様決めから関われる新規開発案件で、かつ小規模なチームであれば、8割以上のケースでいけると思います。
### 機能しないケース
- 上記の技術スタックでは実装できない機能があるケース
- 技術力でブランディングをしたいケース
- エンジニアが仕様に口を出せないケース
- 大規模チーム
    - 「このサービスを作るには◯人必要だな」ではなく「このメンバーで開発するならこの技術」という順番で技術選定するケース

エンジニアが仕様に口を出せない現場とか、大規模チームだとたぶんきついです。
【#2 Java】よく聞くオブジェクト指向とは？

こんにちは、もえです。
今回はJavaを勉強しているとよく出てくる「オブジェクト指向」についてまとめてみました。
# オブジェクトとは
オブジェクト指向は、大きなプログラムを記述しようとしたときに、人間の頭が追い付かず開発に時間がかかったり、完成しても不具合だらけのプログラムになってしまうことが多かったことから誕生した考え方です。
端的に言うと、「ソフトウェアを開発する時に用いる部品化の考え方のこと」です。
この考え方に沿って1つのソフトウェアを複数に部品化すると、プログラムが把握しやすくなり、「人間の頭が追い付かない状況」を避けることが出来るようになります。<br>
オブジェクトとは、端的に言うと「もの」です。
例えば車で考えた時に、タイヤ、シート、エンジンなどひとつひとつがオブジェクトとなります。<br><br><br>
では、オブジェクト指向とは何でしょうか。<br>
オブジェクト指向プログラミングでは、プログラミングの機能や役割を区別し、それぞれの部品(オブジェクト)として組み合わせることで1つのプログラミングを作っています。
Javaでコーディングする際に定義するクラスも、クラスから生成されるインスタンスもオブジェクトです。<br>
オブジェクト指向の3大機能は、以下の3つです。

- カプセル化
  - 属性や操作を、一部の相手からは利用禁止にする機能
- 継承
  - 過去に作った部分を流用し、新しい部品を簡単に作れる機能
- 多様性
  - 似ている2つの部品を「同じようなもの」とみなし、「いいかげん」に利用できる機能


# クラス
クラスは、オブジェクトを作成するための設計図です。
オブジェクトの属性(プロパティ)や振る舞い(メソッド)を記載する。
プロパティは、オブジェクトが持つデータ、メソッドはオブジェクトが持つ機能を指します。<br>
自動車を例に考えてみると、クラスが自動車を作る元となる設計図にあたる。
また自動車の走る、曲がる、止まるといった機能がメソッド、車体サイズ、車体サイズや色、排気量といったスペックがプロパティに対応しています。<br>
# インスタンス
クラスを実体化したものがインスタンスです。
自動車の例で考えれば、設計図を基にして製造された自動車がインスタンスにあたります。
また、クラスからオブジェクトを生成することを「インスタンス化」と呼ぶ。<br>
# コンストラクタ
クラスをインスタンス化する際に初期化処理を行うための特別なブロックです。

- 戻り値を持たないこと(戻り値の型も書かない)
- クラスと同じ名前である

# メソッド
メソッドとは一連の処理を一つにまとめたものです。
メソッドは覚えきれないほどたくさんありますが、自分で自作することが可能です。
というより、自分が実現したいプログラムを書く際、ほとんどの場合に自作のメソッドを作成します。
<br>
# mainメソッド
プログラムを実行する時にシステムから最初に呼び出されるメソッドです。<br><br><br>
今まで「オブジェクト指向って何だろう･･･」と思っていたので、今回少し理解することが出来て良かったです。
また投稿します！
Yay! I'm on Rails! Again

# はじめに
`Ruby on Rails`三回目記念です。
# on Rails!
<img width="960" alt="20200722.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538241/41868324-e952-f069-8781-ad5a81974977.png">
おじさんが帽子をかぶってる！
# 環境
前回、*[Yay! I'm on Rails!](https://qiita.com/superrino130/items/ad5179e825c23204db2f)* は`raspberry pi 3 B+`で、少しの嵌りで乗り切りました。
しかし、次の`raspberry pi zero`は嵌りの連続で、いまだ`rails s`が成功していない状態です。

+ sudo apt-get ruby-dev
+ sudo gem install sassc
+ sudo gem install sassc-rails
+ sudo gem update sass-rails
+ sudo gem install semantic-range
+ sudo gem install webpacker

そこで、気分を変え`Windows10`で`rails s`しました。
# 嵌ったところ
嵌りらしい嵌りはないように感じました。

```
D:\ruby\rails>rails new test271

Done in 3983.70s.
Webpacker successfully installed 🎉 🍰
```
しかし、時間が掛かり過ぎの様な気もします。
# まとめ
+ 初RoR on Windows10
+ 次回こそは外部サーバに挑戦

参照したサイト
*[Rails Girls インストール・レシピ](https://railsgirls.jp/install)*
【Windows10】TortoiseGitを基本的な機能を使ったみた。

##【開発環境】
Windows 10 HOME

Git-2.27.0-64-bit
TortoiseGit 2.10.0.2 - 64-bit


##【目次】

|  | 　　項目　　　　　|
|:----:|:-------------|
| - | [はじめに](#はじめに) |
| - | [使い方](#使い方) |
| - | [git clone](#git-clone) |
| - | [git push](#git-push) |



#はじめに

以前仕事でTortoiseGitを使ったけど
暫く使っていないので基本的な操作を手順もかねて使ってみる。
TortoiseGit、Gitはインストール済でGithubにもアカウント登録済の前提で。



#使い方

【準備】
デスクトップにローカル用のフォルダ「git」を作成。

#git clone

- Gitのリポジトリを複製する。(ダウンロードみたいなイメージです)


![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669259/f7791ec2-384d-6937-7fd2-0ac2600c3481.jpeg)

取り出すフォルダを右クリックし、Git クローン（複製）

![33.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669259/57570233-9eb6-2836-f7f6-0d6b01b24c10.jpeg)



URL欄に、githubなどのリポジトリのURLを入力
「OK」をクリック

![3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669259/23998548-f185-be50-07c2-5b2a54ed8e5a.jpeg)

そうすうるとこんなウィンドウが。

![4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669259/7dd622c0-f8fc-2471-c678-160e665905d9.jpeg)

クローンが完了したみたいです。

![5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669259/fac81763-b812-b530-a618-1bc58b45a93e.jpeg)

先ほどデスクトップに作成した
「git」フォルダの中にファイルがありクローンが完了しました。


##git push

-ローカルリポジトリの(修正した)内容をリモートリポジトリに送信する。

先ほどクローンしたフォルダの中に
README.mdがあるのでそれを追記してみます。


```README.md
# stopwatchApp

made in 2015.11.17
this app is so old that when installing this app.
you will see message just follwing message. so please ignore.
```


```README.md
edited in 2020.07.22
```
上記を追記します。

![6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669259/4513a7c8-97d9-4f5f-77cb-5472b35be0ea.jpeg)

ファイルを保存するとファイル名に赤文字で「!」があると思います。

![7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669259/5e241f24-52bb-0a6a-db2b-b838121cdfe2.jpeg)

ファイルを「右クリック」して Git コミット(C) master　をクリック。

![8.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669259/08285f9c-bd90-a209-a9d4-6b7f332d6fa1.jpeg)

「メッセージ」に「edited in 2020.07.22」と記載。
「コミットしてプッシュ」を押下。

![10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669259/e581d220-bb5a-3415-b4dc-920153a20de6.jpeg)

プッシュが完了したようです。

githubを見てみると、、

![11.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/669259/0c7a647b-fa3d-110b-40fd-c63560e3808e.jpeg)

README.mdに追記した部分とメッセージに記載したものが
が反映されているようです。
mixhostで.htaccessを編集する

デフォルトは非表示なので下記で表示させる必要があるようです

[ファイルマネージャーで非表示ファイル（.htaccessなど）を表示する方法](https://help.mixhost.jp/hc/ja/articles/115003742092-%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%9E%E3%83%8D%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%BC%E3%81%A7%E9%9D%9E%E8%A1%A8%E7%A4%BA%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB-htaccess%E3%81%AA%E3%81%A9-%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95)
あれってこの言語でどうやって書くのだろう？を解決するHyperpolyglot

1つのプログラミング言語ですべて完結すればいいけど、実際には複数の言語を使って組み合わせて使うのが一般的。そのときに直面するのが「あれ、これこの言語でどうやって書くのだろう？」の問題です。

実際に自分も仕事のメインプロジェクトではサーバーサイドはPython3でフロントエンドはTypeScriptを使っており、よくどっちがどっちかわからなくなって困ります。

この問題を解決するために最近、[Hyperpolyglot](https://hyperpolyglot.org/)を使い始めました。

Hyperpologlotは言語間のイディオムの違いを表でまとめてくれているサイトで、オブジェクトもしくは機能毎にイディオムをまとめてくれているサイトです。

自分のプロジェクトにずばりあう比較表は用意されていないのですが、Node.jsとPythonの比較でも基本的には問題ないので、自分は[Scripting Languages I: Node.js, Python, PHP, Ruby - Hyperpolyglot](https://hyperpolyglot.org/scripting)をよく使っています。node.jsではlodashを使って書いてあるところもあるので、今時そんなん使っているやついるんか…と思わんでもないですが。

## 余談

これをいちいち調べるのも面倒なので、エディタが間違ったらsuggestしてほしいところ。すでにそのようなプロジェクトはあるのか？なければ自分で作ろうかなと考えています。

## 参考

- [Hyperpolyglot — A Useful Side-By-Side Programming Reference Sheet](https://fossbytes.com/hyperpolyglot-a-useful-side-by-side-programming-reference-sheet/)
- [これは便利!プログラマーのためのチートシート集「Hyperpolyglot」 \| マイナビニュース](https://news.mynavi.jp/article/20160905-a090/)
AWS SageMaker Studioで"Auth token containing insufficient permissions"が出てアクセスできないときの対処

## ポリシーをアタッチしたのにSageMaker Studioへの画面遷移がエラーになる！

SageMaker Studioを利用する際に`AmazonSageMakerFullAccess`ポリシーがアタッチされているのに画面遷移しようとすると`"Auth token containing insufficient permissions"`というエラーメッセージがでて`HTTP403 Forbidden`になってしまうケースに遭遇しました。

該当のエラーメッセージで検索しても[AWSのフォーラムが1件](https://forums.aws.amazon.com/thread.jspa?threadID=316990)しかヒットせず、しかも誰からも返信がなく解決策を得ることが出来ずに途方に暮れました。

## 原因は何だったのか

結論から言うとIAMのカスタムポリシーの不備でした。

```json
{
    "Statement": [
        {
            "Sid": "SourceIPRestriction",
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "オフィスのグローバルIPアドレスその1",
                        "オフィスのグローバルIPアドレスその2"
                    ]
                }
            }
        }
    ]
}
```

みたいなカスタムポリシーが前任者によって設定されていたのですが、これだと「オフィス内から**以外**のIPアドレスからのアクセスをすべて弾き」ます。
しかしSageMaker StudioはAWSによりリダイレクトされるため、アクセス元が（おそらく）AWS内のIPアドレスになっているため弾かれた、というのが事の真相のようでした。

## どうやって対処したのか

[aws:ViaAWSService](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-viaawsservice)条件キーを設定しました。
aws:ViaAWSServiceはAWSが代わりにリクエストした場合（リダイレクトなど）を対象にするかどうかを条件に含めることが出来ます。

```json
{
    "Statement": [
        {
            "Sid": "SourceIPRestriction",
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "Bool": {
                    "aws:ViaAWSService": "false"
                },
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "オフィスのグローバルIPアドレスその1",
                        "オフィスのグローバルIPアドレスその2"
                    ]
                }
            }
        }
    ]
}
```

こうすることで「**AWSによるリクエストではない場合に**オフィス以外からのアクセスを弾く」という設定になりました。

---

該当するケースが少ない現象だと思いますがどなたかの参考になれば。
AWS CLIをインストールせずにサクッと使用する方法

# AWS CLIとは

AWS Command Line Interface (AWS CLI) は、コマンドラインシェルでコマンドを使用して AWS サービスとやり取りするためのオープンソースツールです。

詳しくはこちら↓
[AWS Command Line Interface とは](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-welcome.html)

# サクッと触ってみたいけどインストールが面倒
ローカルのLinuxマシンにインストールして触ってみようと思いましたが、インストールが中々面倒。
AWS CLIのインストール方法についてはこちら↓
[Linux で AWS CLI バージョン 1 をインストールする](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-linux.html)
結構面倒くさいです。


# インストールせずにAWS CLIを使う方法

ここからが本題ですが、
**AWS EC2**で**Amazon Linux**（あるいは**Amazon Linux2**）を建てれば、
そのインスタンスでAWS CLIが使用できます。

実はAWS CLIバージョン1は、**Amazon LinuxおよびAmazon Linux2にプレインストール**されています。
というお話でした。
勉強のためにAWS CLIを触りたいということであれば、
**EC2無料枠のt2.micro**を建てちゃえば良さそうです。
【#1 Java】Javaの基本の基～勉強前の大前提～

こんにちは、もえです。
今回は私がプログラミング言語に触れるきっかけになったJavaについて書いていきたいと思います。


# そもそもJavaとは
Javaとは、プログラムを作るために利用する、コンパイルが必要なプログラミング言語の一つです。
Javaを使えば、様々なコンピュータで動作する多様なプログラムを開発することが出来ます。<br>
Javaを習得したプログラマーは現在市場で最も求められているので、将来を考えるならとりあえずJavaの勉強を始めてみるといいかもしれません。<br>
ただし、Javaは他のプログラミング言語と比べ少し変わった特徴があります。

- Javaの特徴
  - 学びやすく標準的な基本文法
  - 大規模開発を支援するオブジェクト指向に対応
  - 豊富に準備された便利な命令群
  - Windows、Mac、Linuxどんな環境でも同じように動作する汎用性


Javaはとにかくどこでも動く！をモットーに作られた言語です。
そのため特定のコンピュータが理解出来る機械語に翻訳するのではなく、どのコンピュータでも使える中間言語というものを作成します。<br>
実行する時は、それぞれのコンピュータにインストールされたJava仮想マシン(JavaVM : Java Virtual Machine)が機械語に翻訳し、実行します。
プログラムを実行するのに「日本語→中国語」「日本語→ドイツ語」に翻訳するのではなくて、まずは必ず英語に翻訳してから他の言語に翻訳するというイメージを持つと分かりやすいと思います。<br>
# Javaのソースコードを書いてみる
Javaファイルは、テキストエディタで「.java」という拡張子を付ければ完成しますが、実行環境を用意しないと実行できません。
私は[AdoptOpenJDKを使う](https://www.gwtcenter.com/using-adoptopenjdk)を参考に環境を構築しました。<br>
# Javaの統合開発環境
Javaはファイルを作成後、ソースコードに対してコンパイルというプログラムの翻訳作業をして、バイトコード(byte code)に変換してから実行します。
ちなみにこの翻訳作業を行う翻訳ソフトウェアをコンパイラといます。
さらにコンパイル後、インタプリンタ(interpreter)というソフトウェアに対してバイトコードの実行を指示します。
このインタプリンタはJavaVMという仕組みを内部に持っており、バイトコードをCPUが理解出来るマシン語に変換します。

人間が書くプログラムは、このようにしてコンピュータが分かるように翻訳して初めて実行されますが、そのような設定を自らするような開発をすることはほとんどありません。<br>
実際には、Java開発に必要な「コーディング」「コンパイル」「テスト」「デバッグ」など、プログラム開発の効率化を図れるようにした「統合開発環境(IDE)」を利用して行います。
これにはいくつか種類がありますが、自分に合った開発環境を整えましょう。
ちなみに私はEclipseとIntelliJ IDEAを使用しており、主にEclipseを使用しています。
開発環境の選択にあたっては、[【初心者向け】Java開発ツールの種類を画像付きで解説](https://eng-entrance.com/java-tool)を拝読しました。<br>
# フレームワーク
プログラム開発には統合開発環境という便利な環境があり、さらにその開発の補助となるフレームワークというものが存在します。
これは全体の処理の流れがあらかじめ実装されていて、その中の一部の具体的な処理を自分で実装してはめ込めるようになっているシステムです。
このフレームワークを活用することで、開発者は開発の効率化を図ることが出来ます。<br><br>
Javaの基本的な話はこんな感じかな？
では、また投稿します！
Pygameで紫の雨を降らせる方法

#0.はじめに
今回作るものがどういう感じで動くのか見てみたい、この記事を読むのが面倒くさい方は[こちら](https://youtu.be/nuLHApl4cGI)(Youtubeの動画)をご覧ください。

#1.実装
```purple_rain.py
import pygame
import random

WIDTH = 640
HEIGHT = 360

win = pygame.display.set_mode((WIDTH,HEIGHT))

class Drop:
    def __init__(self,win):
        self.x = random.uniform(0,WIDTH)
        self.y = -1
        self.yspeed = random.uniform(1,3)
        self.win = win
        self.length = random.uniform(10,20)

    def fall(self):
        self.y += self.yspeed

    def show(self):
        pygame.draw.rect(self.win,(138,43,226),(self.x,self.y,2,self.length))

def drawWindow(rain):
    for water in rain:
        water.show()
        if water.y <= 360:
            water.fall()
        else:
            rain.pop(rain.index(water))

    pygame.display.update()

rain = []
run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    win.fill((230,230,250))
    
    if len(rain) < 500:
        rain.append(Drop(win))

    drawWindow(rain)
pygame.quit()
```
クラスを使ったので長くなってしまいましたが、やってることは雨をランダムな長さ、位置、速度で落としていき画面の外に消えたら追加というのをやっています。簡単ですね！
#最後に
今回作ったものは[Youtube](https://youtu.be/nuLHApl4cGI)でも解説しているのでそちらも良かったらご覧ください。質問、アドバイスがあればぜひコメントをよろしくお願いします。
ElixirでBigQueryにPOSTしたい

[google_api_big_query](https://github.com/googleapis/elixir-google-api/tree/master/clients/big_query)を使ってBigQueryにDatasetやテーブルを作りたかった。
しかし、テーブルは取得できるのに何故かPOSTの処理はできないのでハマってしまったので解決方法を残す。
(`Insufficient Permission`と出ていた)

## 対象読者
- 未来の僕
- ElixirでBigQueryにデータを突っ込みたいと思っている人で `Insufficient Permission`って言われて困ってる人


## 結論
`goth`でトークンを取得するときに以下のようにしていた。

```elixir
# これだとPOST系の処理は失敗する
{:ok, token} = Goth.Token.for_scope("https://www.googleapis.com/auth/cloud-platform.read-only")
GoogleApi.BigQuery.V2.Connection.new(token.token)
```

```elixir
# これが正解
{:ok, token} = Goth.Token.for_scope("https://www.googleapis.com/auth/cloud-platform")
GoogleApi.BigQuery.V2.Connection.new(token.token)
```

`Goth.Token.for_scope("URL")`に指定したURLに `read-only`が指定されていた...
過去に一度BQからレコードを取得する処理を書いたときのコードを何も考えずにコピペしたためGET系の操作はできたがPOST系の処理ができないことに気が付かなかった...

##　感想
気がついてしまえばなんてことは無いんだけどここに至るまでにいろいろなことを試しました。
(サービスアカウントを一時的にProjectOwnerに指定してみたりとかソースコードを追いかけたり。)
結果的にはBigQueryのAPI周りに詳しくなったので良かったのですが何も考えずにコピペするのはだめですねぇ。。。(´；ω；｀)
これで分かった！最短マッチ

# はじめに

正規表現で最短マッチというのがあるが、いくつかサンプルで動作を見ながら、どういう使い方をするのか確認してみよう。

# 内容

`str = "ああああ「あ」い「うう」ええ「おおお」かかか"`
このような文字列の中から、括弧で囲まれた部分のみ抽出したい（つまり、「あ」「うう」「おおお」の３ヶ所を抽出したい）場合、最短マッチを利用する事になります。

##### その前に、簡単な動きから確認してみよう。

**＋**は、１文字以上でマッチするため、この例ではマッチしません。

```
str = "ああああ「」"
puts str.scan(/「.+」/)
=>マッチしない
```

**＊**は、０文字以上でマッチするため、この例ではマッチします。

```
str = "ああああ「」"
puts str.scan(/「.*」/)
=>「」
```

##### 次の場合はどうだろうか

**あ**の文字が１文字あるためマッチします。

```
str = "ああああ「あ」"
puts str.scan(/「.+」/)
=>「あ」
```

こちらも、**あ**の文字が０文字あるためマッチします。結果的に同じです。

```
str = "ああああ「あ」"
puts str.scan(/「.*」/)
=>「あ」
```

##### 少し、本題に入ります

この例だと、最初に出現した、**「**と、最終に出現した、**」**で囲まれた部分でマッチしてしまいます。やりたい事はこれではありません。

```
str = "ああああ「あ」い「うう」ええ「おおお」かかか"
puts str.scan(/「.+」/)
=>「あ」い「うう」ええ「おおお」
```

##### で、どうすればいいか

**？**を付加すれば、最短マッチをしてくれるようになります。

```
str = "ああああ「あ」い「うう」ええ「おおお」かかか"
puts str.scan(/「.+?」/)
=>「あ」「うう」「おおお」
```

おめでとうございます。:clap::clap::clap:

##### こうすればどうなる？

**＋**の代わりに、**＊**にしても結果的には同じではありますが・・・

```
str = "ああああ「あ」い「うう」ええ「おおお」かかか"
puts str.scan(/「.*?」/)
=>「あ」「うう」「おおお」
```

**「」**をマッチさせたいか、させたくないかで、**＋**と**＊**を使い分ける事になります。

**「」**をマッチさせたくない場合は、１文字以上でマッチする、**＋**を使います。

```
str = "ああああ「あ」い「うう」ええ「おおお」かかか「」"
puts str.scan(/「.+?」/)
=>「あ」「うう」「おおお」
```

**「」**をマッチさせたい場合は、０文字以上でマッチする、**＊**を使います。

```
str = "ああああ「あ」い「うう」ええ「おおお」かかか「」"
puts str.scan(/「.*?」/)
=>「あ」「うう」「おおお」「」
```

C'est fini :sweat_smile:
crondが死んでたので zabbix で死活を監視する話

いつの間にか crond が死んでいました..。

Linux OOM Killerに殺されたのか、なにか他に原因があったのかは定かではありません。
そのせいで、let's encrypt の自動更新がされず、証明書が期限切れを起こしました。

これはいかん。

zabbixでcrondが死んだら警告を出さねば!

....アイテムがねぇ...orz

作ればいいんだろ、作ればよ〜。

とりあえず zabbix server で新規テンプレート Crond を作ります。
アイテムに crond を keyが proc.num[crond] で。

これでおおよそ大丈夫のはずですが、まれに
proc.num[crond] が取れていないzabbix-agentが...

そういった場合は、agent側から死活をサーバーに通知します。
crondの死活は status で確認できます。
/etc/rc.d/init.d/crond status

runningという文字があれば正常。なければ異常。
 runningをカウントすることで確認できます。

/etc/rc.d/init.d/crond status|grep -e running |wc -l

その結果を zabbix-agent から出力します。
zabbix_agentd.conf
UserParameter=crond,/etc/rc.d/init.d/crond status|grep -e running |wc -l

Zabbixサーバー側でcrondをアイテムに追加し、トリガーを設定します。


監視中の数十あるzabbix-agentにコツコツ追加すればいいんだろ？クソがぁ！
exp_01/ exp_02/ exp_03/ .../ 「このディレクトリ何やったっけ？」を解消したい！

# こんな経験ありませんか？
- なんのディレクトリか忘れた
- 先輩からもらったデータのディレクトリ構造がなんのこっちゃわからん
- 初めて使うフレームワークのディレクトリ構造のメモがほしい

# つくったもの
[ls-Improved](https://github.com/ShotaroKataoka/ls-Improved)というコマンド上で動作する`ls`っぽいコマンドです．


# どんなもの？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368778/baa14596-4672-e12a-6f2d-910ecdaa0538.png)

このように，ディレクトリの説明をつけることができます．また，少しだけ`ls`をリッチに表示してくれます．
例は[rails tutorial](https://railstutorial.jp/)の説明をrailsのデフォルトディレクトリの説明文としてつけました．

# こんなことができます！
- 機械学習等の実験パラメータの整理
![lsi_using.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368778/2fe7fdfb-9437-bce8-b852-c27fefee37f3.png)

- チーム開発におけるディレクトリ構造のコメント
![image (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368778/6e8ad456-9d5d-78b6-f1f6-e776239046d9.png)

- ターミナル映え
![image (11).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368778/1edbb0c3-9312-38f4-3612-871e297cae7a.png)


# 導入方法
ローカルにpython環境が必要です．一応2系でも3系でも動きます！
`pip install ls-Improved`
python環境さえあれば，コマンドプロンプト等でも動きます．

# 基本的な使い方
## `lsi`
ファイルと説明文が付いたディレクトリを表示してくれます．説明文が無い時は`Dir`がデフォルトで付きます．
![image (12).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368778/7d6accbd-6423-070f-66d5-0d81c9b5fe3d.png)



## `lsi -F`
ファイルのみの表示をしてくれます．
![image (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368778/4fdec4bb-0f17-5c9d-aa67-7efa380fff7a.png)


## `lsi -D`
説明文が付いたディレクトリを表示してくれます．
![image (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368778/02571911-d355-5d75-3c08-56cec6b34bf9.png)


## `lsi -a`
隠しファイルも含めて全て表示してくれます．
![image (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368778/196c6c9a-113f-df5a-4207-681d4623cde2.png)

## `lsi -s "<検索ワード>"`
このようにして，説明文から該当するディレクトリを検索することが可能です．
![image (7).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368778/50201685-7bfc-34be-6a80-add738f7ce45.png)

ディレクトリと説明文の両方で検索することができます．
![image (9).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368778/58831a32-34b0-a88a-6551-ce1a626c22a7.png)

## `mkdiri <ディレクトリ名> "<説明文>"`
説明文を付けたディレクトリを作成することができます．
![image (8).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368778/7ba70b3a-4336-ca2f-b9d5-78132ccadff3.png)

### 説明文を装飾する
- `\n`：改行
- `;r;`：赤色にする
- `;g;`：緑色にする
- `;b;`：青色にする
- `;w;`：白色にする
- `;p;`：紫にする
- `;_;`：アンダーラインをつける
- `;e;`：装飾を解除する
![mkdiri_decoration.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368778/767cc6ad-5baa-32f2-ec9e-16b43f1f567c.png)



## `mkdiri -a <ディレクトリ名> "<説明文>"`
すでに存在するディレクトリに説明文を上書きすることができます．
![image (10).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368778/3243d259-4940-cac3-40b0-8f00f76e971a.png)


# どうなってんの？
ディレクトリ内に隠しファイルとして`.description.lsi`というファイルが自動で作成されます．このファイルの中身を直接編集することも可能です．


# 最後に
筆者は`vim`が大好きなので，`Vi-Improved`にあやかって`ls-Improved`と命名しました．ふざけた名前にしてしまい申し訳ありません．🙇‍♂️🙇‍♂️🙇‍♂️🙇‍♂️🙇‍♂️🙇‍♂️🙇‍♂️🙇‍♂️🙇‍♂️🙇‍♂️🙇‍♂️🙇‍♂️🙇‍♂️
MacOS X で vagrant up する時、毎回管理者パスワード聞かれないようにする

## 参考にした記事
https://www.vagrantup.com/docs/synced-folders/nfs.html

/etc/exports の編集、NFSデーモンの起動や停止には管理者権限がどうしても必要ですが、毎回 vagrant 起動時にパスワードを入れるのは流石に面倒なので、以下の方法で対策しました。

## 操作 step by step

```
sudo su -
（パスワードを入力）
vi /etc/sudoers.d/vagrant-syncedfolders
```

※ OsXではメインの /etc/sudoers を編集しなくてOKです。

開いたファイルに以下を追記：

```
Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports
Cmnd_Alias VAGRANT_NFSD = /sbin/nfsd restart
Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /usr/bin/sed -E -e /*/ d -ibak /etc/exports
%admin ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD, VAGRANT_EXPORTS_REMOVE
```

保存して、 Terminal を再起動しましょう。（マックの再起動は不要）
ng addでCannot read property 'options' of undefined (副題: ng new 〜 --minimal の副作用)

作成したAngularプロジェクトに[Angular Calendar](https://github.com/mattlewis92/angular-calendar) をng addでインストールしたところ

```
$ ng add angular-calendar
〜 中略
    Added "angular-calendar" into dependencies
    Added "moment" into dependencies
    Installing angular calendar dependencies...
    Add modules imports options...
Cannot read property 'options' of undefined
```
のようなエラーが発生しました。
npm installといくつかの作業でインストールする方法もドキュメントに書かれているが、このまま放置するのも気持ちが良くないので調べてみました。


## ng add の処理を見てみる
詳しい仕組みは理解してませんがnpmパッケージ内にng addで動作するロジックがあることは知ってたので、node_modules/angular-calendar/schematics/ng-add/index.jsを見てみました。

```typescript:node_modules/angular-calendar/schematics/ng-add/index.js
function default_1(options) {
    return schematics_1.chain([
        addPackageJsonDependencies(options),
        installPackageJsonDependencies(),
        addModuleToImports(options),
        addAngularCalendarStyle(options),
    ]);
}
```

出力されているログから、この配列に登録されているaddModuleToImports等の関数が順に呼出されていることが判ります。
さらに追加でログを仕込んでみると

```typescript:node_modules/angular-calendar/schematics/ng-add/index.js
function addAngularCalendarStyle(options) {
    return function (host, context) {
// 追加
context.logger.log('info', "start addAngularCalendarStyle");
      var libStylePath = 'node_modules/angular-calendar/css/angular-calendar.css';
      utils_1.addStyle(host, libStylePath, options.projectName);
// 追加
context.logger.log('info', "end addAngularCalendarStyle");
      return host;
    };
}
```
```console
    start addAngularCalendarStyle
Cannot read property 'options' of undefined
```
Angular CalendarのCSSファイルをangular.jsonに書き込む処理(utils_1.addStyle)の中でエラーが起きたようです。

```typescript:node_modules/angular-calendar/schematics/utils/config.js
function addStyle(host, stylePath, projectName) {
    var workspace = config_1.getWorkspace(host);
    var appConfig = getAngularAppConfig(workspace, projectName);
    if (appConfig) {
        appConfig.architect.build.options.styles.unshift(stylePath);
        appConfig.architect.test.options.styles.unshift(stylePath);
        writeConfig(host, workspace);
    }
```

おそらくappConfig.architect.buildかappConfig.architect.testのどちらかがundefinedだろうということで、angular.jsonをみてみると確かにarchitect > testがありませんでした。

## 原因
このプロジェクトはng new 〜 --minimalで作成していました。

```
$ ng new --help
中略
  --minimal 
    When true, creates a project without any testing frameworks. (Use for learning purposes only.)
```
テストフレームワークなしでプロジェクトを作成するのでangular.jsonにarchitect > testがないという落ちでした。
--minimalが学習目的だけとはありますが、それは「ちゃんとテストしろよ!」的な思想的な話かと思ったら、まさかこのような形で躓くとは想定外でした。
[Rails]deviseにカラムを追加する

## なんで書いた
deviseでユーザー認証を作るとemailとpasswordは最初からあるけど、ユーザー登録するのに名前は必要でしょ。といつも思ってるのでメモっとく。

## 前提
- 既にdeviseのgem追加してること
- `rails g devise User`してあること

## カラムを追加する

DBにカラムを追加するときは、以下のような書き方をするのが一般的です。
とりあえず、そのマイグレーションファイルが何をするためのファイルがわかる名前にしましょう。

名前つけるとき、`AddColumnToUsers`って書いて「ユーザーテーブルにカラム追加する」みたいな意味だと、次カラム追加する時名前困るので注意。そもそもカラムを追加することになる＝DB設計ちゃんとしろってことなのかと思ったりしてますが、この辺はまだまだ知識が浅いので強い方教えて欲しいです。

```shell
$ rails generate migration Addカラム名Toテーブル名　追加するカラム:データ型
```

```shell
$ rails generate migration AddNameToUsers name:string
```

```ruby:XXXXXXXXXXX_add_name_to_users.rb
class AddNameToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :name, :string
  end
end
```

```shell
$ rails db:migrate
```

## ControllerとViewの修正

```erb:registrations/new.html.erb
<h2>Sign up</h2>

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= render "devise/shared/error_messages", resource: resource %>

	# ここから追加
　<div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name, autofocus: true %>
  </div>
	# ここまで

  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
  </div>

  <div class="field">
    <%= f.label :password %>
    <% if @minimum_password_length %>
    <em>(<%= @minimum_password_length %> characters minimum)</em>
    <% end %><br />
    <%= f.password_field :password, autocomplete: "new-password" %>
  </div>

  <div class="field">
    <%= f.label :password_confirmation %><br />
    <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
  </div>

  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>
```

Viewから飛んできたnameの情報を受け取れるようにしたいので、`
application_controller.rb`に以下を追記します。

```ruby:application_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
  end
end
```

以下のコードを`application.html.erb`へ記述します。
`current_user`はdeviseが提供しているメソッドの一つで、ログインしているユーザーの情報を取得することができます。

```erb:application.html.erb
<% if current_user.present? %>
  <p>こんにちは、<%= current_user.name %>さん！</p>
<% else %>
  <p>こんにちは、ゲストさん！</p>
<% end %>
```

うまく表示されていればOK！

<img width="523" alt="スクリーンショット 2020-07-22 17.47.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/436363/a7abba39-4443-57c4-1728-96d87958c7b0.png">
PRML　演習問題 1.28（基礎）　解答 

## 問題
> 1.6節で、エントロピー$h(x)$のアイディアを、確率分布$p(x)$を持つ確率変数$x$の値を観測することによって増える情報量として導入した。また、変数$x, y$が$p(x,y)=p(x)p(y)$となって独立なときは、エントロピーは加法的で、$h(x,y) = h(x)+h(y)$となることを見た。この演習問題では、$h$と$p$の間の関数関係$h(p)$を導く。まず、$h(p^2)=2h(p)$となることを示し、数学的帰納法により、正の整数$n$に対し$h(p^{n})=nh(p)$となることを示せ。さらに、正の整数$m$に対し、$h(p^{n/m})=(n/m)h(p)$が成り立つことを示せ。このことから$x$が正の有理数のとき、$h(p^x)=xh(p)$が成り立つが、これは連続性により正の実数値の場合も成り立つ。最後にこのことから$h(p)$が$h(p)\propto\ln{p}$の形を取らなければならないことを示せ。

## 前提条件

```math
\begin {align*}
p(x,y)=p(x)p(y)
\tag{ex1.28.1}
\end {align*}
``` 

```math
\begin {align*}
h(x,y) = h(x)+h(y)
\tag{ex1.28.2}
\end {align*}
``` 

## 方針
この問題は、
1. $h(p^2)=2h(p)$となることを示す
2. 数学的帰納法により、正の整数$n$に対し$h(p^{n})=nh(p)$が成り立つことを示す
3. 正の整数$m$に対し、$h(p^{n/m})=(n/m)h(p)$が成り立つことを示す
4. $h(p)$が$h(p)\propto\ln{p}$の形を取らなければならないことを示す
という4段階の証明になっている。

## 証明1
> $h(p^2)=2h(p)$となることを示す

この証明は(ex1.28.2)を用いることによって簡単に示すことができる。
 
```math
\begin {align*}
h(p^2) =& \ h(p\cdot p)\\
=& \ h(p) + h(p) \\
=& \ 2h(p)
\end {align*}
```  

## 証明2
> 数学的帰納法により、正の整数$n$に対し$h(p^{n})=nh(p)$が成り立つことを示す。

- $n = 1$の時

```math
\begin {align*}
h(p) = h(p)
\end {align*}
```  

よって$n = 1$の時$h(p^{n})=nh(p)$は成り立つ

- $n = k$の時($k$は2以上の自然数)

```math
\begin {align*}
h(p^{k})=kh(p)
\end {align*}
```  

が成り立つとすると、

- $n = k+1$の時

```math
\begin {align*}
h(p^{k+1}) =& \ h(p^k, p)\\
=& \ h(p^k) + h(p)\\
=& \ kh(p) + h(p)\\
=& \ (k + 1)h(p)
\tag{ex6.3.1}
\end {align*}
```  

よって、$h(p^{k+1})=(k+1)h(p)$が成り立つ。
このことから数学的帰納法より正の整数$n$に対し$h(p^{n})=nh(p)$が成り立つ

## 証明3
> 正の整数$m$に対し、$h(p^{n/m})=(n/m)h(p)$が成り立つことを示す

証明2では自然数について等式が成り立つことを示したが、ここではさらに拡張して正の分数においても成り立つことを示す。この時はまだ自然数についてのみ等式が成り立っていることに注意して式変形をする。

```math
\begin {align*}
h(p^{n/m}) =& \ nh(p^{1/m})\\
=& \ \frac{n}{m} mh(p^{1/m})\\
=& \ \frac{n}{m} h(p)
\end {align*}
```  
よって$h(p^{n/m})=(n/m)h(p)$が成り立つことは示された

## 証明4
> $h(p)$が$h(p)\propto\ln{p}$の形を取らなければならないことを示す

まず、$p = q^x$とおいてやると、

```math
\begin {align*}
\frac{h(p)}{\ln{p}} =& \ \frac{h(q^x)}{\ln{q^x}}\\
=& \ \frac{xh(q)}{x\ln{q}}\\
=& \ \frac{h(q)}{\ln{q}}\\
\therefore h(p) =  \ \frac{h(q)}{\ln{q}}\ln{p}
\end {align*}
```  
よって$h(p)\propto\ln{p}$の形を取ることが示された。
これは情報量を表す関数$h$が満たすべき条件を表している。
参考：http://prml.yutorihiro.com/chapter-1/1-28/
flaskアプリをdockerfileを使用しherokuへデプロイ

今回は完成しているフラスクアプリをdockerfileから動かしherokuでデプロイするまでを記述します。
ネットを探してもあまりいい記事が見つからずあまり普段はほとんど見ることのない公式のドキュメントを見て実行することにしました。
[heroku公式ドキュメント](https://jp.heroku.com/deploy-with-docker)

###Dockerfile
```dockerfile
FROM ubuntu:latest

RUN apt-get update
RUN apt-get install python3 python3-pip -y

COPY . /home
WORKDIR /home

RUN pip3 install -r requirements.txt
RUN pip3 install email_validator

RUN ls /home

ENV FLASK_APP app.py
RUN echo $FLASK_APP

RUN flask db init
RUN flask db migrate
RUN flask db upgrade
RUN ls /home

CMD flask run -h 0.0.0.0 -p $PORT
```
herokuにdockerとしてデプロイする時に重要なのは下の行です。
CMD flask run -h 0.0.0.0 -p $PORT
$PORTについてはheroku側でうまくやってくれるらしいです。

###app.pyのrunさせるとこ
```python
if __name__ == '__main__':
    app.run(host='0.0.0.0',debug=True)
```
host='0.0.0.0'にしないといけない

###herokuへデプロイ作業
```bash
$ mkdir project
$ cd project
$ git clone <自分で作成したdockerfileが置いてあるリポジトリ>
$ heroku login
$ heroku create
$ heroku container:push web
$ heroku container:release web
$ heroku open
```
<>の中以外はそのまま入力しました。
###修正してまたデプロイするとき
```
$ heroku container:push web
$ heroku container:release web
```
Google Cloud StorageのURLを取得するのにハマった話

Cloud Functionの中で特定の画像のURLを取得したかったのだが、かなり手こずった。
忘れないようにメモしておく。

# １．getDownloadURLを使用してURLを取得しようとする
→Cloud Functionの中からはgetDownloadURLは使えない。

理由↓
https://firebase.google.com/docs/storage/web/download-files?hl=ja
iOSアプリからはこのgetDownloadURLが使えていたし、Cloud Functionの中でもこれで取得したかったが、
どうやらNode.jsではこれは使えないらしい。
Javascriptのクライアントにはあるが、Node.jsにはない。
どこかに公式の表があったが忘れてしまった。
とにかくNode.jsでは使えない。
https://firebase.google.com/docs/storage/admin/start?hl=ja

# ２．署名付きのURLを取得しようとする
→よくわかんないけどエラーでできなかった
（参考URL）
https://qiita.com/nobu17/items/6ef14d5fd0355e90f5cf

↓実際に取得したURL
https://storage.googleapis.com/playbox-53558.appspot.com//thumbnail.jpg?GoogleAccessId=playbox-53558%40appspot.gserviceaccount.com&Expires=16447017600&Signature=bT98s5UVYJow8rvPDfoWy%2BH%2B3eddvGtyyYXhWKC6GA4b8%2BAURCLxTnwuyyhgxJzCm78GwUHO1CYrpF%2Bv%2BPqQOkkfDCRlvaqtuK9KZ94vWYedTMcNmrpf1PzlAYOxoMOCUDpqKQFLZQmk79dhWBGaMG8ovT4bpxMHSnm2eVhvpbRJ7gxP9Ux7tUt1caISBeJcXlcxkvgqCtqy2%2BQH2OR%2Fr3qA%2FQSe4jGa40MzaasyBnvL%2FbAO563ztwiSoSRuCunuSamJXHjJ5Rv%2FgpPnm5KUn3Yrv%2B8hd8WuDLTPGZxiTZ0gi4UDlYEs4%2BTdau10Vprtm8s202IloYR6XjIhBkYt2w%3D%3D

# ３．直接URLを記述する
→セキュリティルールの壁があるけどそれを超えれば表示できる
```https://firebasestorage.googleapis.com/v0/b/バケット名/o/パス名?alt=media```
↓参考
https://qiita.com/koinori/items/f8f534598fe8e1c40c7e
↓実際のURL例
https://firebasestorage.googleapis.com/v0/b/playbox-53558.appspot.com/o/thumbnail.jpg?alt=media
ここでのパス名の中に/を入れるとだめなので注意。
encodeURIComponent(パス名)にするといいらしい。

結果として３番で行くことにした。
２番目の証明書付URLでもできそうだけど１週間で見れなくなるとか色々書いてあるので難しそう。

---参考になったURL
https://firebase.google.com/docs/storage/admin/start?hl=ja
↓ここの「Web」っていう項目はCloudFunctionの中では使えない。
https://firebase.google.com/docs/storage/web/create-reference?hl=ja

![スクリーンショット 2020-07-26 7.35.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59336/1ea7def3-38fc-543b-0e7a-a30a1754f3ca.png)

https://firebase.google.com/docs/web/setup?hl=ja#node.js-%E3%82%A2%E3%83%97%E3%83%AA
Ambassador Edge StackでアクセスログをJSON且つカスタムフォーマットにする方法

Ambassador API Gatewayではまだ https://github.com/datawire/ambassador/issues/2593 が対応されていないですが、 Ambassador Edge Stack (API Gatewayの上位互換) ではJSONでカスタムフォーマットを行えるようになりました。
しかし、envoy_log_formatの設定の記述方法が皆無だったので後の人のためにメモを残しておきます。

## 設定ファイルサンプル

```yaml
---
apiVersion: getambassador.io/v1
kind: Module
metadata:
  name: ambassador
spec:
  config:
    envoy_log_type: json
    envoy_log_format: 
      start_time: '%START_TIME%'
      method: '%REQ(:METHOD)%'
      path: '%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%'
      protocol: '%PROTOCOL%'
      response_code: '%RESPONSE_CODE%'
      response_flags: '%RESPONSE_FLAGS%'
      bytes_received: '%BYTES_RECEIVED%'
      bytes_sent: '%BYTES_SENT%'
      duration: '%DURATION%'
      upstream_service_time: '%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%'
      x_forwarded_for: '%REQ(X-FORWARDED-FOR)%'
      user_agent: '%REQ(USER-AGENT)%'
      request_id: '%REQ(X-REQUEST-ID)%'
      authority: '%REQ(:AUTHORITY)%'
      upstream_host: '%UPSTREAM_HOST%'
      upstream_cluster: '%UPSTREAM_CLUSTER%'
      upstream_local_address: '%UPSTREAM_LOCAL_ADDRESS%'
      downstream_local_address: '%DOWNSTREAM_LOCAL_ADDRESS%'
      downstream_remote_address: '%DOWNSTREAM_REMOTE_ADDRESS%'
      requested_server_name: '%REQUESTED_SERVER_NAME%'
      istio_policy_status: '%DYNAMIC_METADATA(istio.mixer:status)%'
      upstream_transport_failure_reason: '%UPSTREAM_TRANSPORT_FAILURE_REASON%'
```

## 簡易説明
デフォルトのログは何を出力しているかは https://github.com/datawire/ambassador/blob/master/python/ambassador/envoy/v2/v2listener.py#L725-L748 に書かれています。
これをそのまま envoy_log_format の形式にしたものが上になります。

## 苦労したところ

https://www.getambassador.io/docs/latest/topics/running/ambassador/ の envoy_log_format に

> Defines the envoy log line format. See this page for a complete list of operators

と書かれてリンク飛んでいろいろ試しましたが、結果的にはDictである必要があるみたいです。

あとは独自のヘッダー等は `%REQ(HOGEHOGE)%` で取れるので煮るなり焼くなりできます。
rubocop導入 how to~

自分用メモ

```yml:rubocop.ymlに以下を追加
inherit_from: .rubocop_todo.yml
```

```text:一時退避をする
rubocop --auto-gen-config 

==>> rubocop_todo.ymlファイルができる
```
#rubocop_todo.yml内の、指示を読み解く
以下ドキュメントや記事を使用

[rubyドキュメント]
(https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Layout/ArgumentAlignment)

[qiitaで翻訳している](https://qiita.com/shibukk/items/8c9362a5bd399b9c56be)

[rubocopドキュメント](https://docs.rubocop.org/rubocop/0.86/cops_naming.html#namingmethodparametername)


```yml:rubocop_todo.yml内を読み解き、コメントアウトする
# Layout/ArgumentAlignment:
#   Exclude:
#     - 'app/models/user.rb'
```

```text:ターミナルで修正
rubocop -a
```

```text:git-statusで変更ファイルを確認
git status
```

```text:git-diffで変更箇所を確認
git diff -- [ファイルへのパス]
``` 


#リモートブランチへ反映
```text:pushする
git add .
git commit -m "message"
git push -u origin HEAD
```

```text:再起動して普段どおり動くかテスト
docker-compose down
docker-compose up
```

#以降、繰り返す