OmniAICreator/Qiita-1.07M huggingface dataset:
就活用ポートフォリオ紹介「俺のラーメン」 〜制作日数・反省点など〜

#はじめに

こんにちは！
しばた[（@shibata0406）](https://twitter.com/shibata0406)と申します！
地元の食品メーカーにて営業をやっておりましたが、現在はYouTube活動をしながら転職活動中！
興味のある方はぜひ見ていってください〜
→[YouTubeチャンネル「ポントイズ」](http://www.youtube.com/c/ポントイズPonToys)
<br>
今回は、ポートフォリオとして作成した「ラーメンサイト」をご紹介いたします。

#ポートフォリオ「俺のラーメン」
<img width="1440" alt="スクリーンショット 2020-03-11 20.35.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569978/1cfe5136-3501-6621-4ca4-0893a0d472ae.png">
ラーメン好きの(僕の)ラーメン好きによる(僕による)ラーメン好きの為の(僕のための)ラーメンサイトです。

URL : https://myramen.herokuapp.com/
GitHub : https://github.com/shibata0406/myramen.git

#なぜ作ったのか
ラーメンが大好きだからです。w
今まで学んだ技術のアウトプットとして、自分でワクワクしながら作れるサイトを目指しました。
ラーメン好きの方はぜひ見てやってください。
※深夜に閲覧する際はご注意ください(飯テロ)。

#制作日数
最初にデプロイするまでおよそ30日、
その後修正や機能追加を経て現在の形に至るまでおよそ30日、
計60日でした。

#こだわり
非ログイン時にどこまでアプリを使用できるか、
→トップページのラーメンや、ユーザーページの閲覧は可。投稿や検索、フォロー、お気に入り等はログイン必須にしました。
また、どんなサイトなのかパッと見で分かりやすいよう、トップページにはラーメンの一覧を表示。
ユーザー層を考え、サイトのお洒落さよりも、わかりやすさを重視しました。

#機能一覧
###バックエンド
●Ruby 2.5.1
●Rails 5.2.4.1
　・投稿機能
　・スクレイピング
　・検索
　・ログイン、ログアウト(devise)
　・ランキング
　・ページネーション(kaminari)
　・画像アップロード(Active Storage、Mini Magick)
　・コメント・評価
　・フラッシュメッセージ
　・Faker(faker 2.8)
###フロントエンド
・お気に入り機能(jQuery,Ajax)
・フォロー機能(jQuery,Ajax)
・タブメニュー(jQuery)
・ドロップメニューで削除、編集(jQuery)

※CSSのフレームワークBootstrapは使用せず、一から組み立て実装しました。
###インフラ
・Amazon S3
・heroku
・MySQL(管理ソフト Sequel Pro)

※今回、初のデプロイはHerokuで行いました。AWSに挑戦できるよう学習中でございます。。

#使い方・デモ
###①トップページ
こちらがログイン後のトップページです。
左右で「ラーメン一覧表示」「評価数ランキング」を表示しています。
<img width="1440" alt="スクリーンショット 2020-03-11 20.35.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569978/1cfe5136-3501-6621-4ca4-0893a0d472ae.png">
###②ラーメン詳細
気になるラーメンをクリックすると、この画面に移行します。
![ラーメン詳細完成！.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569978/bdae2069-19ee-d3e9-a220-ca9eb0929925.png)
###③コメント評価の投稿
ラーメン詳細ページから「このラーメンを評価する」で評価画面へ移行。
<img width="1440" alt="75137033-cf373980-5729-11ea-93f7-07b7cadbc15b.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569978/a0f7ec16-bb41-e178-2f44-655d7002dbbc.png">
###④ラーメン検索画面
検索窓にキーワードを打ち込み、当てはまるラーメンを表示します。
![検索完成！！！.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569978/d2914d0f-7bf0-4634-102f-495a03e5aab1.png)
###⑤ユーザー画面
ユーザーの過去のレビューやお気に入り、フォロー・フォロワー、ユーザー情報の編集画面です。
![ユーザー完成！！.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569978/e542fcbd-bde5-c9f6-a5d8-f7d25eab5176.png)
#反省点

・DockerやCircleCI等の技術が活用できていない。
・テストが十分に書けていない。
・レスポンシブ対応が不十分。
・趣味のサイトのため、日常に役立つものではない。

など多くの反省点や課題が残っています。
次のポートフォリオで課題を解決していきます！
#最後に
今回初めて一からwebサービスを作りデプロイしましたが、
あーだこーだ考えながらコードを書くのが最高に楽しかったです！
もっと設計段階に時間を割くべきだったと反省点はありますが、何より自分の考えたサービスが形になっていくという事に、ワクワクが止まりませんでした。
今回はラーメンサイトよりスクレイピングで情報を取得し、お気に入りやラーメン検索、投稿機能を実装するなど、今の技術を表現するポートフォリオとなりました。
今後はより日常生活で活用できたり、友達や家族で実際に使える便利なサービスを作りたいと考えています。
(お笑いが大好きなので、お笑い好きが集まるマッチングサイトなど作りたい。。。)

今回無事にデプロイできたのも、もくもく会で出会った方やTwitter、Qiitaや友人エンジニアなど多くの方にアドバイスを頂いたおかげです。大変感謝しております。

今後もプログラミングの学習を続けながら転職活動を頑張りたいと思います。
Qiitaも不定期で投稿しておりますので、もしよければフォローのほどよろしくお願いいたします。


【参考記事】
[就活用ポートフォリオとしてWebサービス「Asobi」を作りました。](https://qiita.com/sasakura_870/items/1eb666c80c4ceb815dd8)
たのしい Foldable

Foldable がよくわからない人のための3分くっきんぐです！

# Foldable って何？
どんなに複雑なデータ構造でも、**一列のフラットなデータ構造と同一視できる**という性質のことです。

例えば、

```haskell
data Tree a = Leaf a | Node (Tree a) (Tree a)

      Node
      /  \
  Node    Node
  /  \    /  \
 a    b  c    d
```

これを、

```haskell
  Node
  /  \
 a   Node
     /  \
    b   Node
        /  \
       c   Node
           /  \
          d   (EMPTY)
```

これと同じものとみなしても良いという性質が Foldable。

ちなみに `List` は何もしなくてもこの形なので、一番分かりやすい Foldable の例です。

# foldr って何？
フラットにしたデータ構造の**節と末端を好きなものに置き換えるだけの関数**です。

```haskell
data List a = Nil | Cons a (List a)
(:) = Cons

1 : 2 : 3 : Nil
  Cons
  /  \
 1   Cons
     /  \
    2   Cons
        /  \
       3    Nil

foldr :: forall f a b. Foldable f => (a -> b -> b) -> b -> f a -> b
foldr f init = ?データ構造ごとに定義されています

foldr (+) 999

   (+)
  /   \
 1    (+)
     /   \
    2    (+)
        /   \
       3     999

(1 : (2 : (3 : Nil))) ==> (1 + (2 + (3 + 999)))
==> 1005


foldr (-) 456

   (-)
  /   \
 1    (-)
     /   \
    2    (-)
        /   \
       3     456

(1 : (2 : (3 : Nil))) ==> (1 - (2 - (3 - 456)))
==> -454


foldr (append <<< show) "END"

 (append <<< show)
  /     \
 1    (append <<< show)
       /     \
      2    (append <<< show)
            /     \
           3      "END"

(1 : (2 : (3 : Nil))) ==> (show 1 <> (show 2 <> (show 3 <> "END")))
==> "123END"
```

# foldl は？
**foldr で節を「右向きの関数合成」に置き換えたときに得られるもの**です。
実質的には、「逆向きにフラットにしたデータ構造」の節と末端を好きなものに置き換えるだけの関数と考えられます。

```haskell
a : b : c : Nil
  Cons
  /  \
 a   Cons
     /  \
    b   Cons
        /  \
       c    Nil


foldl :: forall f a b. Foldable f => (b -> a -> b) -> b -> f a -> b
foldl f = flip $ foldr (\left right -> flip f left >>> right) identity

  (flip f _ >>> _)
  /     \
 a    (flip f _ >>> _)
      /     \
     b    (flip f _ >>> _)
          /     \
         c      identity

==> (flip f a >>> (flip f b >>> (flip f c >>> identity)))

==> \init -> init # flip f a # flip f b # flip f c # identity
==> \init -> (((init `f` a) `f` b) `f` c)

          `f`
         /   \
       `f`    c
      /   \
    `f`    b
   /   \
 init   a


foldl (+) 999

          (+)
         /   \
       (+)    3
      /   \
    (+)    2
   /   \
 999    1

(1 : (2 : (3 : Nil))) ==> (((999 + 1) + 2) + 3)
==> 1005


foldl (-) 456

          (-)
         /   \
       (-)    3
      /   \
    (-)    2
   /   \
 456    1

(1 : (2 : (3 : Nil))) ==> (((456 - 1) - 2) - 3)
==> 450
```

この絵を見れば、foldr と foldl で節を構成する関数の引数順序が入れ替わっている理由も自然に分かりますね？

# おわり
* Foldable なら、データ構造をフラットなものとみなすことができる。
* foldr は、フラットなデータ構造の節と末端を好きなものに置き換える関数。
* foldl は foldr の逆向きだけど、foldr で節を関数合成に置き換えたものとも言える。

どのように再帰してるかとか、右から計算とか左から計算とか、そういうむずかしい感じに考えなくても大丈夫。
要は単なる**データ構造の式変換**でしかないわけです。かんたんかんたん。
【Vue.js】storeの変更を検知して、dataプロパティの値を変更する。【Vuex】

## はじめに
Vuexのstoreの値が変更された際、それをコンポーネント側で検知して、
コンポーネント内のdataプロパティの値を変更するサンプルです。

## ソース

```sample.js

const state = {
  sampleList: []
}

const getters = {
 sampleList: state => state.sampleList,
}
 
const actions = {
 SET_SAMPLE_LIST({ commit }, values) {
  commit('SET_SAMPLE_LIST',values);
 }
}
 
const mutations = {
 SET_SAMPLE_LIST(state, { values }) {
  state.sampleList = values;
 },
}
 
export default {
  namespaced: true,
  state,
  getters,
  actions,
  mutations
}
```

```sample.vue

// 中略・・・

<script>
export default {
  data() {
    return {
      list: {
        datasets: [
          {
            numbers: [10, 20, 30],
          }
        ],
        labels: ["Red", "Yellow", "Blue"],
      }
    };
  },
  computed: {
    sampleList: function() {
      return this.$store.getters["sample/sampleList"];
    }
  },
  watch: {
    sampleList(values) {
      if (values) {
      	let numberList = [];
        let labelList = [];
        for (let i = 0; i < values.length; i++) {
          const value = values[i];
          numberList.push(data.number);
          labelList.push(data.label);
        }
        // this.$setでdataを変更。
        this.$set(this.list.datasets[0], "numbers", numberList);
        this.$set(this.list, "labels", labelList);
      }
    }
  },
};
</script>

```
[自習] カイジ E カードの勝率を求める

# E カードとは？
『賭博黙示録カイジ』に登場する2人対戦カードゲーム．
詳細は[Eカード（賭博黙示録カイジ - Wikipedia）](https://ja.wikipedia.org/wiki/%E8%B3%AD%E5%8D%9A%E9%BB%99%E7%A4%BA%E9%8C%B2%E3%82%AB%E3%82%A4%E3%82%B8#E%E3%82%AB%E3%83%BC%E3%83%89)などを参照のこと．

# ルール
- カードは「皇帝」「市民」「奴隷」の3種類がある
- 皇帝は市民に勝利でき，市民は奴隷に勝利でき，奴隷は皇帝に勝利できる（cf. じゃんけん）
- 奴隷側の手札は「奴隷カード1枚，市民カード4枚」とする
- 皇帝側の手札は「皇帝カード1枚，市民カード4枚」とする

その他「必ず奴隷側が先にカードを出す」「カードを見ることなく無作為に出してはならない」等のルールは無視し，**完全なる「運ゲー」である**ことを仮定する．

# 考えられるゲーム結果を書き出す
以下，記号は次の通りとする．

- ○: 奴隷側の勝利（皇帝側の敗北）
- ×: 奴隷側の敗北（皇帝側の勝利）
- △: 引き分け

## 1ターン目
|-|帝|民|民|民|民|
|:-:|:-:|:-:|:-:|:-:|:-:|
|隷|○|×|×|×|×|
|民|×|△|△|△|△|
|民|×|△|△|△|△|
|民|×|△|△|△|△|
|民|×|△|△|△|△|

- 奴隷側の勝率は $1/25$
- 奴隷側の敗率は $8/25$
- 引き分けとなる確率は $16/25$

## 2ターン目
|-|帝|民|民|民|
|:-:|:-:|:-:|:-:|:-:|
|隷|○|×|×|×|
|民|×|△|△|△|
|民|×|△|△|△|
|民|×|△|△|△|

- 奴隷側の勝率は $1/16$
- 奴隷側の敗率は $3/8$
- 引き分けとなる確率は $9/16$

## 3ターン目
|-|帝|民|民|
|:-:|:-:|:-:|:-:|
|隷|○|×|×|
|民|×|△|△|
|民|×|△|△|

- 奴隷側の勝率は $1/9$
- 奴隷側の敗率は $4/9$
- 引き分けとなる確率は $4/9$

## 4ターン目
|-|帝|民|
|:-:|:-:|:-:|
|隷|○|×|
|民|×|△|

- 奴隷側の勝率は $1/4$
- 奴隷側の敗率は $1/2$
- 引き分けとなる確率は $1/4$

## 5ターン目
|-|帝|
|:-:|:-:|
|隷|○|

- 奴隷側の勝率は $1$
- 奴隷側の敗率は $0$
- 引き分けとなる確率は $0$

# 奴隷側の勝率計算
奴隷側の勝率は

- 1回目で勝利する確率
- 1回目で引き分けとなり，2回目で勝利する確率
- 1, 2回目で引き分けとなり，3回目で勝利する確率
- 1, 2, 3回目で引き分けとなり，4回目で勝利する確率
- 1, 2, 3, 4回目で引き分けとなり，5回目で勝利する確率

をすべて足すことで求められるから，

```math
\frac{1}{25}
+\frac{16}{25}\cdot\frac{1}{16}
+\frac{16}{25}\cdot\frac{9}{16}\cdot\frac{1}{9}
+\frac{16}{25}\cdot\frac{9}{16}\cdot\frac{4}{9}\cdot\frac{1}{4}
+\frac{16}{25}\cdot\frac{9}{16}\cdot\frac{4}{9}\cdot\frac{1}{4}\cdot1
=\frac{1}{5}
```
となる．

# 皇帝側の勝率計算
皇帝側の勝率も

- 1回目で勝利する確率
- 1回目で引き分けとなり，2回目で勝利する確率
- 1, 2回目で引き分けとなり，3回目で勝利する確率
- 1, 2, 3回目で引き分けとなり，4回目で勝利する確率
- 1, 2, 3, 4回目で引き分けとなり，5回目で勝利する確率

をすべて足すことで求められるから，

```math
\frac{8}{25}
+\frac{16}{25}\cdot\frac{3}{8}
+\frac{16}{25}\cdot\frac{9}{16}\cdot\frac{4}{9}
+\frac{16}{25}\cdot\frac{9}{16}\cdot\frac{4}{9}\cdot\frac{1}{2}
+\frac{16}{25}\cdot\frac{9}{16}\cdot\frac{4}{9}\cdot\frac{1}{4}\cdot0
=\frac{4}{5}
```
となる．

# 余談
計算してみようと思ったきっかけは，はじめしゃちょーの畑の動画である．
<blockquote class="twitter-tweet" data-theme="light"><p lang="ja" dir="ltr">今日の出荷！！<br><br>【カイジ】デスゲームで負けた人は１年間〇〇します <a href="https://t.co/g0nr3vfVp8">https://t.co/g0nr3vfVp8</a> <a href="https://twitter.com/YouTube?ref_src=twsrc%5Etfw">@YouTube</a>より</p>&mdash; はじめしゃちょーの畑 (@hajimesyachoHTK) <a href="https://twitter.com/hajimesyachoHTK/status/1220274050829709313?ref_src=twsrc%5Etfw">January 23, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
【確率】モーメント母関数が、「モーメントの母関数」であることを理解する

#この記事で書いたこと
モーメント母関数が、「モーメントの母関数」であることを導きます。

#はじめに

連続確率変数$X$に対して、モーメント母関数は

```math
M_X(t) = E \left[ e^{Xt} \right]
```
で定義されます。
この確率変数の確率密度関数$f(x)$を有する場合、モーメント母関数は

```math
M_X(t) = \int_{-\infty}^{\infty} e^{xt} f(x) dx
```
とも書けます。

モーメント母関数は確率分布と一対一対応しているので、確率分布の性質を論ずる時に便利であったり、
M次の原点のまわりのモーメントを簡単な微分演算で導出できるので重宝されます。


さて、ここからが本題なのですが、多くの解説サイトでは、モーメント母関数が「モーメントの母関数である」ことを、下記のように解説しています。

> モーメント母関数を$t$で$m$回微分してから$t=0$を代入すると$m$次のモーメントを求められる

引用：[モーメント母関数の定義と使い方。モーメント母関数から期待値と分散を求めてみよう](https://atarimae.biz/archives/11419)


こういった解説は、モーメント母関数の定義式から与えられる性質なので正しいのですが、正直私にはピンと来ませんでした。
確かにこの性質はモーメント母関数が、正真正銘モーメントの母関数であることから成り立つのでしょう。
しかし、もっと直接的に、「モーメントの母関数」を愚直に変形していけばモーメント母関数の定義式$M_X(t) = E \left[ e^{Xt} \right]$が導かれることを示してほしいのです。
ネット記事ではそのような記事は見つけられなかったので、書きました。

# 確率のモーメント
まず確率のモーメントをおさらいします。確率変数$X$の原点のまわりの（１次）モーメントは

```math
m_1 = E \left[ x  \right] = \int_{-\infty}^{\infty} x f(x) dx
```
で与えられます。モーメントという言葉は力学のモーメントとは無関係ではなく、距離$x$と力$f(x)$のそれと同一であるように見えるのは重要なことです。
n次のモーメントは

```math
m_n = E \left[ x^n  \right] = \int_{-\infty}^{\infty} x^n f(x) dx
```
と書けます。[^1]

[^1]: 余談ですが、モーメントの考え方を分散に当てはめれば、分散$V[X] = E \left[ (x-\mu)^2  \right]$は平均まわりの2次モーメントにすぎません。


# 母関数
次に母関数のおさらいをします。

## 通常型母関数

ある数列$\\{a_n\\}$の通常型母関数は

```math
A(t) = \sum_{x=0}^{\infty} a_n t^n
```
と表します。この母関数は数列と一対一対応なので、数列の性質を論ずる時に便利です。

ここで、例えば全ての$n$で$a_n = 1$であるとき、母関数は

```math
A(t) = 1+t+t^2+\cdots = \frac{1}{1-t}
```
と閉じた形に収束します。
しかし、$a_n$が非線形的に増加する場合、すなわち$a_n$の収束半径が広い場合、$A(t)$は収束せず閉じた形になりません。
この場合、指数型母関数を使うことで解決できる場合があります。

## 指数型母関数

ある数列$\\{a_n\\}$の指数型母関数は

```math
A(t) = \sum_{x=0}^{\infty} a_n \frac{t^n}{n!}
```
と表します。
分母に$n$の階乗項があるのでより広い収束半径の数列も閉じた形になります。


# モーメントの母関数
それでは本題です。モーメントの母関数$A(t)$は以下のように書けます。
n次のモーメントを数列$a_n$は

```math
a_n =  E \left[ x^n  \right]
```
と表され、この指数型母関数$A(t)$は

```math
A(t) = \sum_{n=0}^{\infty} E \left[ x^n  \right]\frac{t^n}{n!}
```
と表せます。
平均値の性質を利用すれば、

```math
\begin{align}
A(t) &= \sum_{n=0}^{\infty} E \left[ x^n  \right]\frac{t^n}{n!} \\
&= E \left[ \sum_{n=0}^{\infty} x^n \frac{t^n}{n!} \right] \\
&= E \left[ \sum_{n=0}^{\infty} \frac{(tx)^n}{n!} \right]
\end{align}
```

と変形できます。ここで、$e^x$の0のまわりのテイラー展開を思い出すと、収束半径$\infty$で、

```math
e^x = \sum_{n=0}^{\infty} \frac{x^n}{n!}
```
であるので、先ほどの式は、

```math
\begin{align}
A(t) &= E \left[ \sum_{n=0}^{\infty} \frac{(tx)^n}{n!} \right]　\\
&= E \left[ e^{tx} \right]
\end{align}
```
と変形できます。これは冒頭で定義した$M_X(t)$と一致します！

#おわりに
モーメント母関数は、嘘偽りなく「モーメントの母関数」であることを示せました。
便利な関数なので、その意味や裏付けを知ることはとても有意義ですね。
この他に、離散確率変数の確率母関数なども勉強すると理解が深まります。
【爆速】30分で出来る！React-Nativeでチャットアプリ！

# 目次
* 環境構築
    * コマンドインストール
* ライブラリインストール
* App.jsを書き換え
* 動作確認

誤字脱字あると思いますが、温かい目で見てください！🙇‍♂️💦
誤りなどありましたら、ご指摘頂けると幸いです💦

## 環境構築
### コマンドインストール
詳しくは [公式 [ React Native CLI Quickstart ]](https://reactnative.dev/docs/environment-setup) を参照
( ※Expoは利用していません。Expoにするとスタンドアローンでは無くなる為)

**macbookで、Androidアプリ起動を対象**とします。
また、Android StudioとXcodeがmacbookにインストールされているという前提で行います。

すでに自分が作成したシェルスクリプトが存在するので、[こちら](https://github.com/susu-to-susu/ChatApp)をGithubからクローンして利用してください。以下の手順で実行します。

``` bash
# Githubからダウンロード
$ git clone https://github.com/susu-to-susu/ChatApp.git

# ディレクトリ移動
$ cd ChatApp

# 環境構築用シェルスクリプトを実行
$ sh settingEnvironment.sh

# もし自分用にカスタマイズしたいアプリを作成したい場合
# 雛形作成
$ create-react-native-app ../myApp
```

## ライブラリインストール(アップデート)
チャットアプリのライブラリをインストール(アップデート)します。

自分が作成した時とみなさんが動かそうとする時で、バージョンが違う可能性があるため

``` bash
# 未インストール
$ npm install react-native-gifted-chat --save

# アップデート
$ npm update react-native-gifted-chat
```

## App.jsを書き換え

**Githubからダウンロードした物を利用する場合は、すでに書き換えてあるので、そのままでよいです。**

先ほど作成した雛形(myApp)を利用したい方は、中にあるApp.jsを書き換えます。

```jsx:App.js
import React from 'react';
import { GiftedChat } from 'react-native-gifted-chat';

export default class App extends React.Component {

    componentWillMount() {
        this.setState({messages : []});
    }

    reply(){
        return {
            _id: 1,
            text: 'こんにちは！',
            createdAt: new Date(),
            user: {
                _id: 2,
                name: 'TEST USER',
                avatar: 'https://placeimg.com/140/140/any',
            }
        };
    }


    onSend(messages = []) {
        this.setState(previousState => ({
            messages: GiftedChat.append(GiftedChat.append(previousState.messages, messages), this.reply()),
        }))
    }

    render() {
        return (
                <GiftedChat
                messages={this.state.messages}
                onSend={messages => this.onSend(messages)}
                user={{
                    _id: 1,
                    name: 'ME',
                    avatar: 'https://placeimg.com/140/140/any'
                }}
                />
        );
    }
}

```

[ ※ここは注意 ]
ちなみにApp.jsのファイルの最後の行には、Enterで改行してください。
**改行が１行無いと動かないです。**

## 動作確認

以下のコマンドで実行します。

``` bash
# 起動
$ npx react-native run-android
```

実際に動かしてみた画面です

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417396/bc04c533-3034-c82a-cd4b-da2a28a3f36e.jpeg" width="50%">

**停止**
「 Ctrl + C 」で停止してください。

## 参考
* サンプルコード
    * https://github.com/susu-to-susu/ChatApp
* React-Native公式
    * https://reactnative.dev/docs/getting-started
* 【React Native】Expo + react-native-gifted-chat でチャットアプリを作る
    * https://rennnosukesann.hatenablog.com/entry/2018/05/27/155309
【Git】よく使うコマンドまとめ

## はじめに
開発時に自分がよく使うgitのコマンドをまとめました。
コマンドを網羅しているわけではありませんので、ご了承ください。

## 開発スタート時に使うコマンド

### リポジトリをローカルに複製する

```shell
git clone [リポジトリURL]
```

`リポジトリURL`はGithubで言うと、ここですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230436/ae3ad139-a970-e0bb-8e6a-c38ab32104ca.png)

### gitの設定を確認する

```shell
git config --list --[local|global|system]
```

- system: システム全体
- global: ユーザ全体
- local: 対象リポジトリのみ

同じ設定項目が存在する場合、`system < global < local`の優先順位で適用されます。

出力はこんな感じです。

```
core.symlinks=false
core.autocrlf=true
core.fscache=true
color.diff=auto
color.status=auto
color.branch=auto
color.interactive=true
:
:
```



### 設定変更

```shell
git config --global user.name [名前]
git config --global user.email [メールアドレス]
git config --global core.editor [エディタ名]
git config --global core.quotepath false
git config --global --add merge.ff false
git config --global --add pull.ff only
```


## 開発中に使うコマンド
### ブランチを作成

```shell
git checkout -b [新しいブランチ名] [元のブランチ名]
```

### 今どうなっているか確認

```shell
git status
```


### （何らかの編集をした後）ステージング

addする内容を確認する場合は、

```shell
git diff
```

で確認してから、

```shell
git add [コミットしたいファイルorディレクトリ]
```

### git add の取り消し

```shell
git reset HEAD [ファイル名]
```

### コミット

```shell
git commit -m "コミットメッセージ"
```

### コミットの取り消し

```shell
git reset --hard HEAD^ # 直前のコミットの取り消し
```

`--hard`: ワーキングディレクトリの内容も取り消し
`--soft`: ワーキングディレクトリの内容はそのまま

### 打ち消しコミット

```shell
git revert [打ち消したいコミットのID]
```


### 追跡しているブランチを確認

```shell
git branch -vv
```

### リモートブランチとの差分を確認

```shell
git diff [リモートブランチ名]..HEAD
```

### リモートリポジトリに反映

```shell
git push -u origin [ブランチ名]
```

`-u`をつけると追跡ブランチとして設定されるので、次のpushからは、

```shell
git push
```

でOK。


### リモートリポジトリの変更を取り込む前に差分を確認

```shell
git diff HEAD..[リモートブランチ名]
```

### リモートリポジトリの変更を取り込む

```shell
git pull --rebase
```

### ブランチの付け替え

```shell
git rebase --onto [新しい派生元ブランチ名] [現在の派生元ブランチ名] [付け替えるブランチ名]
```

### 開発（作業）の途中で別のブランチに切り替えなければならないとき

```shell
git stash
```

で変更を退避させてから、

```shell
git checkout [ブランチ名]
```

でブランチ切り替え。

### stash（退避）した内容を確認する

```shell
git stash show -p stash@{N}
```

### stash（退避）した内容を戻す

```shell
git stash apply
```

## たまに使うコマンド
### ブランチ削除

```shell
git branch -d [ブランチ名]
```

### ブランチ強制削除

```shell
git branch -D [ブランチ名]
```

### リモートリポジトリ追加

```shell
git remote add [リポジトリ名] [リポジトリURL]
```

### コミット履歴を確認

```shell
git log
git log --oneline # 1行で簡易表示
git log --merges
```

### HEADの履歴を確認

```shell
git reflog
```

### コミットを取り込む

```shell
git cherry-pick [取り込むコミットID]
git cherry-pick [コミットID 1]..[コミットID 2] # コミットID 1～2を取り込む（※コミットID 1は含まないので、コミットID 1を取り込みたいときはコミットID 1の一つ前のコミットを指定する）
```
Mac から同じ LAN 内に接続した Raspberry Pi の IP アドレスを調べる

Mac から Raspberry Pi に接続するために IP アドレスを調べる必要が生じたのでメモ。

## 環境

環境は下記のとおり。

```shell
$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.3
BuildVersion:   19D76
```

Raspberry Pi は Mac と同じ LAN に接続している。

## arp-scan をインストールする

IP アドレスを調べるために `arp-scan` をインストールする。

Homebrew からインストールできる。

```shell
brew install arp-scan
```

## Mac の IP アドレスを調べる

システム環境 > ネットワーク

から Mac の IP アドレスを確認する。

![ネットワーク.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6294/66575c30-bbf3-c491-73e3-419d47c134ea.png)

ここでは `192.168.3.2` であることが分かる。 

## LAN のインターフェイス名を調べる

LAN に接続しているインターフェイス名を調べるために `ifconfig` を使う。

```shell
$ ifconfig | grep -B 4 192.168.3.2
en1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        options=400<CHANNEL_IO>
        ether a4:83:e7:15:4f:26
        inet6 fe80::6:67bd:fa54:173f%en1 prefixlen 64 secured scopeid 0x7
        inet 192.168.3.2 netmask 0xffffff00 broadcast 192.168.3.255
```

ここでは `en1` であることが分かる。 

## Raspberry Pi の IP アドレスを調べる

先にインストールした `arp-scan` を使って Raspberry Pi の IP アドレスを調べる。

管理者権限が必要なので `sudo` を付けて実行する。 

```shell
$ sudo arp-scan --localnet --interface en1 | grep 'Raspberry Pi'
192.168.3.19    dc:a6:32:71:f1:e6       Raspberry Pi Trading Ltd
```

`192.168.3.19` であることが分かった。めでたい :tada:
herokuCLIのセットアップからPostgreのグレードアップまでのコマンド履歴（MacOS）

# 経緯

Djangoプロジェクトのデプロイにherokuを活用しています。
さいきんMacを買い換えたので、heroku　CLIが入っていない状態から、Postgreのインスタンス（herokuだとAddOnですかね）をグレードアップした時の履歴と簡単解説メモです。

hobby-dev 無料 レコード数：1万（ここから）
hobby-basic 9ドル/月 レコード数:1000万（こちらへアップグレード）

# 環境

MacOS Mojave

# 手順

### 手順1:heroku CLI導入

```
$brew install heroku/brew/heroku
$heroku login
```

### 手順2:herokuのプロジェクトを確認

```
$heroku list
=== ****@gmail.com Apps
****1
****2
****3
****4
```
これでアプリ名がわかりました。

### 手順3:

手順2で調べた対応するアプリ名を"<アプリ名>"に置き換えてください。

アプリ名は、"****1"とかのことです。

```
//アプリの状況チェック
$heroku pg:info -a <アプリ名>

//新規データベースインスタンス生成
$heroku addons:create heroku-postgresql:hobby-basic -a <アプリ名>
```
↑で出てくる環境変数名"HEROKU_POSTGRESQL_******_URL"はすぐあとで使います。

```
//DBが壊れないように？一時稼働停止
$heroku pg:wait -a <アプリ名>

//プロジェクトのメンテナンスモードをONにする
$heroku maintenance:on -a <アプリ名>
```

次がちょっとだけ注意です。既存データベースの情報を新データベースにコピペするコマンドです。

```
//既存データベースの情報を新データベースにコピペ
$heroku pg:copy DATABASE_URL as HEROKU_POSTGRESQL_******_URL -a <アプリ名>
```

↑の環境変数名の"******"の部分が見た記事と違っていたので、そのままコピペをするとダメですので、環境変数名チェックをしてコピペをカスタムするのが必要です。

```
//新規DBにDjangoプロジェクトを接続。
$heroku pg:promote HEROKU_POSTGRESQL_******_URL -a <アプリ名>

//接続確認
$heroku pg:info -a <アプリ名>

//メンテナンスモード解除
$heroku maintenance:off -a <アプリ名>
```

# 参考になった記事

Heroku CLIの導入と初期設定
https://blog.katsubemakito.net/macos/setup_heroku-cli

Heroku操作 CLI
https://qiita.com/ntkgcj/items/9e812220881d671b6bff

herokuのpostgresqlをアップグレードする
https://qiita.com/ozepon/items/6cc30737e96f01b4da67

Heroku の PostgresQL を無料の hobby-dev から 9ドル/月の hobby-basic にアップグレードする【LGTMoon】
https://www.utakata.work/entry/20190226/1551145958
nuxt.config.jsで.envで定義した値を使う方法

.envを導入すると

```js
process.env.XXX
```

で`.env`に定義した値を環境変数としてとってこれます。
便利ですね。

ただ`nuxt.config.js` のファイル内で`process.env.XXX`を使うと`.env`で定義した値を取得できませんでした。
調べたところ以下の記述

```js:nuxt.config.js
import dotenv from 'dotenv'
dotenv.config()
```

を書いてやることで`process.env.XXX`が効くようになりました。
pytest でも datetime.datetime.now() を mock したい!

# TL; DR

こんな感じでモックすると、`datetime.datetime.now()` をモックできます。この方法だと `datetime.datetime` の他の関数はそのまま使うことができます。

```python
from unittest.mock import MagicMock

def test_mocking_datetime_now(monkeypatch):
    datetime_mock = MagicMock(wraps=datetime.datetime)
    datetime_mock.now.return_value = datetime.datetime(2020, 3, 11, 0, 0, 0)
    monkeypatch.setattr(datetime, "datetime", datetime_mock)
```

# 背景

現在時刻を取得する処理をテストするときに、`datetime.datetime.now()` をモックしたいことがあります。このとき、単純に `monkeypatch.setattr`でモックしようとしても、`datetime.datetime.now()` は built-in のため、モックすることができません。

```python
import datetime
from unittest.mock import MagicMock

FAKE_NOW = datetime.datetime(2020, 3, 11, 0, 0, 0)

def test_mocking_datetime_now_incorrect(monkeypatch):
    monkeypatch.setattr(datetime.datetime, "now", MagicMock(return_value=FAKE_NOW)
```

```
>       monkeypatch.setattr(datetime.datetime, "now", MagicMock(return_value="hoge"))
E       TypeError: can't set attributes of built-in/extension type 'datetime.datetime'
```

# そこで MagicMock(wraps=datetime.datetime)

そこで、`MagicMock(wraps=...)` を利用します。wrap 引数にモックするオブジェクトを指定して、通常のメソッドを wrap したオブジェクトに流しつつ、必要なメソッドをモックすることができます。

```python
from unittest.mock import MagicMock

def test_mocking_datetime_now(monkeypatch):
    # now() だけをモックした datetime_mock を作成
    datetime_mock = MagicMock(wraps=datetime.datetime)
    datetime_mock.now.return_value = datetime.datetime(2020, 3, 11, 0, 0, 0)  # これで now() をモック

    # datetime.datetime を datetime_mock で置き換え
    monkeypatch.setattr(datetime, "datetime", datetime_mock)

    # 以下、datetime.datetime.now() を使うテスト
```

# 余録

[stackoverflow のトップ回答](https://stackoverflow.com/q/20503373/7449523) がイケてなかったのでムシャクシャして[(回答して)やった](https://stackoverflow.com/a/60629703/7449523)。反省はしてない。

トップ回答の方法だと datetime.datetime の他のメソッドが呼べないんですよね。こちらの方法なら、`datetime.datetime.fromisoformat()` とか、他のメソッドは通常通りに呼べるぞ、と。

ちなみに別解としては、 `pytest-freezegun` を使うというのがあり、こっちのほうがメジャーっぽいですね。参考: [Pytest現在時刻テスト（日時固定） \- Qiita](https://qiita.com/Bashi50/items/cd7ec741d5015984798e)
JQueryのextendメソッド

#JQueryのextendメソッド（備忘録）

オブジェクトのマージにはextend()を使用する。

```javascript
var obj1 = {name: "taro", age: 20};
var obj2 = {age: 25, sex: "man"};

$.extend(obj1, obj2);

console.log(obj1);

結果
{name: "taro", age: 25, sex: "man"}
```

第一引数に空オブジェクトを指定すると元オブジェクトを壊さずに済む。

```javascript
var obj1 = {name: "taro", age: 20};
var obj2 = {age: 25, sex: "man"};

var newObj = $.extend({}, obj1, obj2);

console.log(obj1);
console.log(nowObj);

結果
{name: "taro", age: 20}
{name: "taro", age: 25, sex: "man"}
```
Nightwatch clearValueが動かない

## 環境
- node:10.15.3
- nightwatch: 1.3.4
- chromedriver: 80.0.1

## 前提
- Nightwatchでフォームへの入力を模すにはsetValue()を使う
- 既にフォームに何らかの値が入力されている場合、既存の値に付け加えるかたちで入力されてしまう
  - 既存の値をリセットするためにclearValue()を使えと[公式リファレンス](https://nightwatchjs.org/api/setValue.html#apimethod-page)には書かれている

> setValue does not clear the existing value of the element. To do so, use the clearValue() command.

## 問題
clearValue()が動かない。
issueにあがっていた以下の対策を試してみたがダメだった。

- [nightwatch issue#504](https://github.com/nightwatchjs/nightwatch/issues/504)
  - clearValue()をする前に当該要素にclick()すると動くよ
- [nightwatch issue#1592 その1](https://github.com/nightwatchjs/nightwatch/issues/1592)
  - setValue('element', '')で空文字入れるといいよ
- [nightwatch issue#1592 その2](https://github.com/nightwatchjs/nightwatch/issues/1592)
  - setValue()で入れる -> clearValue()で消す -> setValue()でもう1回入れるでうまくいくよ

## 解決法
最終的にこの方針に落ち着いた。

- [nightwatch issue#1592 その3](https://github.com/nightwatchjs/nightwatch/issues/1592)
  - getValue()で要素を取得してその文字数分方向キー右とバックスペースで消す

ただ`setValue()`にRIGHT_ARROWとBACK_SPACEを渡すとエラーが出て自分の環境では動かなかった。

そのため、setValue()の代わりに`browser.keys(key)`を用いてボタン操作をしてやることで期待する操作をすることができた。

```javascript:削除コマンド
const clearCommand = {
  clear: function(browser, locator) {
    return browser
      .waitForElementVisible(locator, 1000)
      .click(locator)
      .getValue(locator, function(result) {
        const length = result.value.length;

        for (let i = 0; i < length; i++) {
          browser.keys(browser.Keys.RIGHT_ARROW);
        }

        for (let i = 0; i < length; i++) {
          browser.keys(browser.Keys.BACK_SPACE);
        }
      });
  }
};
```
org-mksについての覚書

org-mksはorg-captureで実装されている関数。
org-captureのテンプレートの選択時に使われている。

```
(org-mks nil "Title\n" "Prompt"
	 '(("a" "Special a.")
	   ("b" "Special b.")
	   ("q" "Special abort.")))
```

この状態でevalするとメニューを表示したバッファが作られて、mini bufferで入力を待つ。

![testing.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36261/6b75b6e6-5910-3722-9506-4c4e0872ff91.gif)
Gitコマンド覚書き（初学者用）

# はじめに
Git初学者が最初に目にする項目をまとめました。
本当の本当の初学者は「あれもこれも」とあまり手広く見ないほうが理解が定着しやすいと思うので、最も基本的なコマンドだけを記載しています。





# Gitコマンド
### 1.ローカルリポジトリ作成
```
git init
```
**ローカルリポジトリの作成**
<br>

```
git add .gitignore
```
**gitignoreファイルをステージ(インデックス)に登録**
**この処理の後にコミットをしないと登録されない。
<br>



### 2.ステージング〜コミット
```
git add ファイルパス
```
**ステージ(インデックス)に追加**

リポジトリ内の全てのファイルを一括で追加する場合はオプションを追加。
- git add -A
カレントディレクトリ以下の全てのファイルを一括で追加する場合はオプションを追加。
- git add .
<br>

```
git commit -m "コメント"
```
**ステージ(インデックス)のファイルをコミット**

ステージ(インデックス)への登録と、コミットを一度にする場合はAオプションを追加。
- git commit -a -m "コメント"
<br>



### 3.リポジトリの状態確認
```
git status
```
**リポジトリの確認**
<br>

```
git diff
```
**ワークツリーとステージ(インデックス)の差分を確認**
<br>

```
git diff --cached
```
**ステージ(インデックス)と最新のコミットの差分を確認**
<br>



### 4.コミットの確認
```
git log
```
**コミットの確認**

差分付きでコミット履歴を確認する場合はPオプションを追加する。
- git log -p
<br>



### 5.変更取消
```
git checkout -- ファイルパス
```
**ワークツリーの変更を取り消す**
<br>

```
git reset HEAD ファイルパス
```
**ステージ(インデックス)への登録を取り消す**
<br>



### 6.削除
```
git rm ファイルパス
```
**Git管理下のファイルを削除**
**この処理の後にコミットをしないと削除されない。

ディレクトリを削除する場合はRオプションを追加する。
- git rm -r ファイルパス
<br>



### 7.ブランチの操作
```
git branch 作成するブランチ名
```
**ブランチの作成**

ブランチの作成と切り替えを一度にするには以下を使用する。
- git checkout -b 作成するブランチ名
<br>

```
git branch
```
**現在のブランチの確認**
<br>

```
git checkout 切り替え先のブランチ名
```
**ブランチの切り替え**
<br>

```
git diff master
```
**現在のブランチとマスターブランチの差分を確認**
<br>

```
git merge 取得するブランチ名
```
**別のブランチから最新内容を反映させる**
<br>

```
git branch -d 削除するブランチ名
```
**ブランチの削除**

マージ済みでない場合はDオプションを使用する。
- git branch -D マージ済みのブランチ
<br>



### 8.リモートリポジトリ（GitHub）関連
```
git clone GitHubからコピーするURL
```
**リモートリポジトリをクローンする**
<br>

```
git remote -v
```
**リモートリポジトリの設定を確認**
<br>


```
git push origin プッシュするブランチ名
```
**リモートリポジトリ（origin）にファイルをプッシュ**
<br>

```
git pull origin master
```
**リモートリポジトリ（origin）のブランチ（master）をプル**
<br>

```
git fetch origin
```
**リモートリポジトリ（origin）をローカルリポジトリに取り込む**
**この処理の後にマージをしないとワークツリーには反映されない。
<br>

```
git push --delete origin 削除するブランチ名
```
**リモートリポジトリのブランチを削除**
<br>





# おまけ（ターミナルコマンド）
```
cd ~
```
**ルートディレクトリに移動**
<br>

```
mkdir ディレクトリ名
```
**ディレクトリの作成**
<br>

```
ls -a
```
**現在のディレクトリ以下の全てのファイルを表示**
<br>

```
pwd
```
**現在のディレクトリパスを表示**
<br>

```
touch ファイル名
```
**新しいファイルを作成**
<br>

```
cat ファイル名
```
**ファイルの内容を確認**
<br>

```
code ファイルパス
```
**Visual Studio Codeのファイルを開く**
if文練習

## if文を使いこなそう！
問題
あなたは警官です。aとｂ二人の容疑者の取り調べをしています。
どちらも証言がTrue、またはFalseであればその証言はTrueです。
しかしどちらかがFalseでTrueであればその証言はFalse、と出力するメソッドを定義しなさい。

呼び出しは以下のように行う
police_trouble(a, b)

例
police_trouble(true, false) → False
police_trouble(false, false) → True
police_trouble(true, true) → True

上記のような問題があったとしましょう。このようなif文はどのように実装すれば良いでしょうか。
私は最初下記のようなメソッドを実装しました。

```ruby
def police_trouble(a, b)
  if (a == true) && (b == true)
      puts "True"
  elsif (a == false) && (b == false)
      puts "True"
  else
      puts "False"
  end
end

a = true
b = true
police_trouble(a, b)
```
aとbがtrueならTrue
aとbがfalseならTrue
問題の指示通り実装ができていますね。
しかし、これよりももっとシンプルな方法があります。

今回の問題を置き換えて考えると、aとbの値が一致していればTrueと出力すれば良いと言うことになります。
そのため下記のような書き方ができます。

```ruby
def police_trouble(a, b)
  if a && b || !a && !b
    puts "True"
  else
    puts "False"
  end
end

a = true
b = true
police_trouble(a, b)
```
aとbが一致していればTrue、それ以外ならFalseと返すプログラムですね。

考え方を整理して、シンプルに書くことを磨いていきたいです。
PCを使ってAndroidのChromeのデベロッパーツールを表示する方法

AndroidのChromeでの検証でデベロッパーツールを表示したいということがあったので備忘録に…。

## 必要なもの
・検証用Android
・USBケーブル(データの送受信が行えるものであればおそらく動くはず…)
・PC(今回macを使用しました)

## Androidでの準備
### Androidを「開発者モード」にする
1.設定からビルド番号を表示
検証端末によっては以下のように場所が異なるそうです。

```

Android 9（API レベル 28）以上
　[設定] > [デバイス情報] > [ビルド番号]
Android 8.0.0（API レベル 26）および Android 8.1.0（API レベル 26）
　[設定] > [システム] > [デバイス情報] > [ビルド番号]
Android 7.1（API レベル 25）以下
　[設定] > [デバイス情報] > [ビルド番号]
```

2.ビルド番号を７回タップ
端末にパスワードをかけている場合はパスワードを聞かれるので入力してください。

3.USBデバッグを有効にする
「開発者向けオプション」が選択できるようになっているのでそこからUSBデバッグを有効にする

```
Android 9（API レベル 28）以上
　[設定] > [システム] > [詳細設定] > [開発者向けオプション] > [USBデバッグ]
Android 8.0.0（API レベル 26）および Android 8.1.0（API レベル 26）
　[設定] > [システム] > [開発者向けオプション] > [USBデバッグ]
Android 7.1（API レベル 25）以下
　[設定] > [開発者向けオプション] > [USBデバッグ]
```

## 手順
1.端末とPCをUSBケーブルで接続

2.PCのChromeで以下アドレスにアクセス

`chrome://inspect/#devices`
端末の方で許可が求められた時はOKを押してください

3.端末でChromeを開く

4.2の画面にて`inspect`を押下
<img width="440" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410737/27be7d4d-6d8f-6c12-b0b4-7acc56ba40b7.png">
5.デベロッパーツールが表示できる

## まとめ
Console、Elementsなどが使えるのでとても便利ですね…。
思っていたよりも簡単に画面が開けたので「スマホでの細かい見た目を調節したい」や「javascriptでconsoleに表示をしたい」なども簡単にできますね。

### おまけ
macにAndroidを接続しても認識されない場合は以下の手順を行う。

1.PCで「システム情報」を表示
Finderで`[アプリケーション] > [ユーティリティ] > [システム情報]`から表示できます。

2.表示された画面で「製造元ID」を確認する
`[ハードウェア] > [USB] > [(USBで繋いだ端末の)製造元ID]`で見ることができます。

3.ターミナルでコマンドをうつ

```
$ echo "<製造元ID>" >> ~/.android/adb_usb.ini
```
※ `<製造元ID>`　には2で確認した製造元IDを入れる


参考
<a href="https://developer.android.com/studio/debug/dev-options.html?hl=ja">デバイスの開発者向けオプションを設定する</a>
<a href="https://qiita.com/hojishi/items/12b726f8b02ef3d713e4">Android の Chrome で開発者ツールを使う方法</a>
<a href="http://blog.digital-squad.net/article/406937882.html">MacでAndroidを接続しても認識されない場合の対応</a>
S3 のサーバアクセスログから、Athena を使って日毎のリクエスト数を取得する

DB、テーブルは作成済みの前提

## GET、SELECT、他のすべてのリクエスト

```sql
SELECT regexp_extract(time, '\d{2}/\w+/\d{4}') AS by_day 
, count(*) AS cnt
FROM s3_accesslog
WHERE  (operation NOT LIKE '%PUT%'
        AND operation NOT LIKE '%POST%'
        AND operation NOT LIKE '%COPY%'
        AND  operation NOT LIKE '%LIST%')
GROUP BY regexp_extract(time,'\d{2}/\w+/\d{4}')
ORDER BY by_day
```

## PUT、COPY、POST、LIST リクエスト

```sql
SELECT regexp_extract(time, '\d{2}/\w+/\d{4}') AS by_day 
, count(*) AS cnt
FROM s3_accesslog
WHERE  (operation LIKE '%PUT%'
        OR operation LIKE '%POST%'
        OR operation LIKE '%COPY%'
        OR  operation LIKE '%LIST%')
GROUP BY regexp_extract(time,'\d{2}/\w+/\d{4}')
ORDER BY by_day
```

## 参考サイト

* [Amazon Athena S3のサーバアクセスログでS3署名バージョンを確認してみた ｜ Developers.IO](https://dev.classmethod.jp/cloud/aws/20190606-amazon-athena-for-sigv2/)
* [Amazon Athena の Presto 関数 - Amazon Athena](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/presto-functions.html)
fitieが動かない


要するに、jsであとから追加したものには適用されない


```js
this.fitie(document.getElementById('hoge'));
```

ajaxとかなんとかかんとか使ってるとこだと

```js
$('img.hoge').bind('load', function(e){window.fitie(e.target);});
```

ってすると幸せになれるかもしれない
React勉強開始

#どんな記事？
自分用備忘録です。まとめながらやってこうと思います。
マークダウン慣れてないので大目に見て。
今回は公式のReactチュートリアル
https://ja.reactjs.org/tutorial/tutorial.html
やった感想とか書いていきます。
#なぜReactを勉強しようと思ったか。
React勉強しようと思ったのは一昨日。

授業で使ったjspが気に入らなくて、
見た目の再利用しやすいものなにかないかと思って

現在主流っぽいjsのフレームワークのようなものの中から
tomcatとかともワンチャン併用できそうなやつを選んだ。
結局できなさそうなら、まあその時はその時。
#目標
死ぬほど使いやすいToDoアプリ作る。
#1日目 Reactチュートリアル
昨日から今日にかけて勉強したことふわっとまとめていく。
チュートリアルはローカル環境ではなくブラウザ上で進めた。
最終結果までやって追加課題はやってない。

* Reactはコンポーネントベース。
コンポーネントというものを組み合わせて見た目作っていくらしい。

* render()の中でHTMLっぽいもの書く。
コンポーネントはrender()を実装しないといけない。
render()の中で他のコンポーネントをタグみたいな書き方で呼べる。

* stateとprops
  * stateはそのコンポーネントの中で扱う変化していく値。
  * propsは親のコンポーネントから渡される値。propsで足りるならそれで済ます方が丸そう。一元管理したいのと、再利用性の為？
  * どっちもオブジェクト。keyとvalueで個々のパラメータ持ってる

* props渡し方
コンポーネント呼ぶときのタグに属性みたいな感じにkey="value"とか書く
* state入れ方
コンストラクタで作るのと、setState({key:value});
* state,props呼び方
this.state.keyとかthis.props.keyとかでvalue返ってくる。
this以外のstate,propsはチュートリアルでは呼んでなかった。
* ミューテートとイミューテート
オブジェクトの中身を書き換えちゃうのがミューテート。変更を検知しにくいから避けた方がいいらしい。コピーして一部書き換えたオブジェクトを新しく作るといいんだって

* イベントの伝え方
onClick={this.props.onClick}
onClickとしてイベントが起こったのを知りたいメソッド渡しとく

#次やること
ローカルで作る。ファイル分けて作る。それがブラウザで動くとこ確認する。
#続いてやること
react-router-domを知る

#読んでくれてありがと
:v:初心者の備忘録やぞ？:v:読むの大変だったろ:v:ありがと:v:
【Go】HTTPサーバがexitする直前に、後始末処理を仕込む！（コネクション開放とか）

はい:clap:

今回ご紹介するのは、こちらの`nasa9084/syg`です。
https://github.com/nasa9084/syg

* Golang: 手軽にシグナルをListenしてcallback関数を呼ぶ<br />
https://blog.web-apps.tech/go-signal-more-simply/

これを使えば、HTTPサーバがexit（停止）する前に、任意のコードを仕込めてしまいます。
ありがてぇ…ありがてぇ…

# 経緯

GoでHTTPのRESTAPIサービスを作っていて、その中でRedisのコネクションをプールして使いまわす機能を作りました。
そうするとやっぱ、サービスが終了（HTTPサーバが停止）するときにコネクションをちゃんと開放しなきゃですよ。

「でも、`http.ListenAndServe`を実行してるところに`defer func`を仕掛けりゃいいだけッスよね？ ちょろいちょろい♪」

とナメていたら…

`defer func`が実行されない！！

どうやら`os.Exit(code int)`が発動すると、`defer`がかき消されてしまうとのこと。なんてこったい:innocent:

そこで見つけたのがこちらになります。

# 用例

例えばこんな感じで、シグナルを受け取ったときに発動するコールバック関数に、好きな後始末処理を仕込むだけ:thumbsup:

私の場合は、プールしてるRedisのコネクションを`Close()`してます。

```go
package main

import (
	"context"
	"fmt"
	"github.com/go-redis/redis"
	"net/http"
	"os"
	"github.com/nasa9084/syg"
)

var pooledKVSConnectionsMap = make(map[*redis.Options]*redis.Client)

func main() {
	s := &http.Server{Addr: ":8080"}
	waitCh := make(chan struct{})

	cancel := syg.Listen(func(os.Signal) {
		s.Shutdown(context.Background())
		destruct() // <--- 仕込み！
		close(waitCh)
	}, os.Interrupt)
	defer cancel()

	if err := s.ListenAndServe(); err != http.ErrServerClosed {
		fmt.Print("listening server error:" + err.Error())
	}
	<-waitCh
}

func destruct() {
	for _, kvsCon := range pooledKVSConnectionsMap {
		cerr := kvsCon.Close()
		if cerr != nil {
			fmt.Print(cerr)
		} else {
			fmt.Print(kvsCon, " ... closed.")
		}
	}
}
```

# ご注意

`github.com/nasa9084/syg`の`README.md`がちょっと古いのか、そちらの例では`defer cancel()`のコードが抜けているので、作者様サイトの例コードや`syg_example_test.go`などを参照されると良いかと思います。

# さいごに

・・・
というか、そもそも間違ってました・・・

いえ、この記事そのものではないのですが、`go-redis/redis`はあらかじめコネクションプールの仕組みを内包しており、使い手側は気にせず`NewClient()`, `Close()`をして良いそうです:scream:

実際goroutineを使って`NewClient()`, `Close()`を毎度呼ぶコードと比較したのですが、全く問題ありませんでした。

・・・まあ、勉強になったからよし！
if文練習

## 条件分岐を使いこなそう
問題
平日でないまたは休日の場合は「True」と返信し、
休日ではない場合は「False」と条件分岐させるメソッドを実装しなさい。

メソッドの呼び出しは
today_is_vacation(weekend, vacation)

このような問題があったとする。一見何を言っているのかわからないと思うので例を
引数が
(false, false)なら　False(休日出勤)
(true, false)なら　False(平日)
(false, True)なら　True(休日)
(True, True)なら　True(平日休み)
を判別すると言うことである。
これを実装するにはどのようなif文で条件分岐をすれば良いのでしょうか。4通りの引数の渡し方があって一見複雑そうですよね？
しかし、よく考えてみてください。
問題文としては休日ならTrue、違うならFalseを出すだけなので、実は条件分岐はシンプルです。

```ruby
def sleep_in(weekday, vacation)
  if vacation == false
    puts "False"
  else
    puts "True"
  end
end

weekday = true
vacation = false
sleep_in(weekday, vacation)
```
このように条件分岐を記述することで
vacationがtrueだった場合のみTrueを返します。これだけで
休日ならTrue、違うならFalseを出力することができます。

一見複雑に見えても、出力したい結果がなんのかを意識することで問題は以外は簡単だったりします。
問題と向き合って、一番楽な正解を見つけられるクセをつけていきたいですね！
複数リージョンのサービス使用時はMultiple Providerを使用する

複数のリージョンに分散するサービスを作成したい場合、[Multiple Provider](https://www.terraform.io/docs/configuration/providers.html#alias-multiple-provider-instances)を使用すると便利です。

<!-- more -->

- main.tf

```tf
provider "aws" {
  region  = "ap-northeast-1"
  profile = "terraform01"
}

// multiple provider
provider "aws" {
  region  = "us-east-1"
  alias   = "virginia"
  profile = "terraform02"
}
```

- 例えば、globalaccelerator_accelerato.tf

```tf
resource "aws_globalaccelerator_accelerator" "select" {
  provider = aws.virginia
(snip)
```

- 例えば、aws_wafv2_web_acl.tf

```tf
resource "aws_wafv2_web_acl" "select" {
  name        = "aws_wafv2_web_acl"
  scope       = "CLOUDFRONT"
  provider    = aws.virginia

  default_action {
    block {}
  }
(snip)
```

- 例えば、data.aws_lambda_function

```tf
data "aws_lambda_function" "latest_lambda" {
  function_name = "latest"
  provider      = aws.virginia
(snip)
}
```
pipenvの導入(requirements.txtも作る)

# はじめに  

pipenvを導入したので，環境構築方法をまとめます．   
  
余分なパッケージの入っていないrequirements.txtを作成したいので，devオプションを使って，コード内でimportするパッケージと環境構築のためにインストールしたパッケージを分けます．
  
間違いなどありましたら指摘していただけると非常に助かります．  

# 環境
- Windows 10 pro  
- PowerShell 5.1.18362.628  
- Python 3.7.6  
- pipenv 2018.11.26  

# pipenvのインストール
`pip install pipenv`   
  
ローカル環境にpipenvをインストールします.

# 仮想環境の作成
仮想環境を使いたいディレクトリ内に入って 
  
`pipenv install --python=3.7.6`  
      
で仮想環境を作成します．バージョンは適宜使いたいものを指定します.  
  
同時に**Pipfile**と**Pipfile.lock**がカレントディレクトリに作成されます.   
  
`ls`  
  
でファイルが作成されていることを確認できます.

# 必要なライブラリのインストール
`pipenv install [package]`  
  
で仮想環境に入らずに仮想環境内にパッケージをインストールできます．例えば，  
  
`pipenv install numpy`
  
とするとnumpyがインストールできます．

## --dev オプション
`pipenv install --dev [package] `  
  
--devオプションを使うと開発用パッケージとしてインストールできます.  
  
環境構築のために使うパッケージはdevオプションを使ってインストールし，  
コード上でimportするようなパッケージはこのオプション無しでインストールします．

# 仮想環境内のパッケージの確認
`pipenv lock -r`  
  
で仮想環境に入っているパッケージの一覧(--devで入れたもの以外)が表示されます．

`pipenv lock -r -d`  
  
で開発環境のパッケージが確認できます．

`pipenv run pip freeze`  
  
で，入っているすべてのパッケージが確認できます．　　

## requirements.txtの作成

`pipenv lock -r > requirements.txt`  
  
でカレントディレクトリにrequirements.txtの作成ができます  

# 削除とPipfileのクリーン
`pipenv uninstall [package]`  
  
パッケージをアンインストールします
  
`pipenv clean` 
  
パッケージをuninstallしても残っている依存パッケージを削除します．  
  
[参考]https://qiita.com/eduidl/items/c0e8256bb3a5a735d19c　　
  
`pipenv --rm`    
  
仮想環境を削除します．  

# その他コマンド

`pipenv --venv`  
  
仮想環境のpathを取得.仮想環境があるかどうかの確認に使えます．
  
`pipenv shell`  
  
仮想環境に入ります． 
  
`exit`  
仮想環境から出ます 

※PowerShellを使っている場合，pipenvで仮想環境に入っても仮想環境に入っているという表記がありません(わかりにくいです)．  
 
# 追記

jupyter labやVScodeには，仮想環境を選択できる仕組みがあるので，  
`switch kernel`  
などをクリックして仮想環境を変更することができます．
WSL に neovim 0.5.0 を導入する

WSL の ubuntu 18.04 だと neovim が古いパッケージで floating window が使えない、と聞いたので  
最新版を入れて init.vim を読み込ませるところまでやる。

## 手順

公式から linux 向けビルドを落としてくる

```
$ wget https://github.com/neovim/neovim/releases/download/nightly/nvim-linux64.tar.gz
$ tar xzvf nvim-linux64.tar.gz
```

解凍したディレクトリから nvim の実行ファイルを /usr/local/bin へコピー  
runtime がないためこちらもコピー

```
$ sudo cp nvim-linux64/bin/nvim /usr/local/bin/
$ sudo cp -r nvim-linux64/share/nvim /usr/share/
```

この時点で nvim を実行すると  
`E484: Can't open file /share/nvim/syntax/syntax.vim`  
とエラーが出る。これは runtime に関するエラーのため init.vim に下記のように記載する。  
(nvim 起動して `:echo $VIMRUNTIME` するとパスが違うことがわかる)

```
$ mkdir -p .config/nvim
$ nvim .config/nvim/init.vim

let $VIMRUNTIME="/usr/share/nvim/runtime"
set runtimepath+=/usr/share/nvim/runtime
```

記載したら `:wq` で抜けて nvim を起動すると syntax がついていることが確認できる。
[SQL]PostgreSQLのデータをMySQLに移行

PostgreSQLのデータを一旦CSVファイルに出力

```
psql -h ホスト名 -p ポート名 -U ユーザー名 -d DB名 -f select.sql > table.csv
```


```select.sql
copy (テーブル名 or select文) to stdout with csv delimiter ',' escape '\' force quote *;
```

CSVファイルをMySQLにインポート

```
# インポートするCSVのファイル名はMySQLのテーブル名と一致する必要がある
mysqlimport -h ホスト名 -P ポート名 -u ユーザー名 --fields-terminated-by="," --fields-enclosed-by='"' --lines-terminated-by="\n" --local DB名 table.csv
```
Android SDK ToolsがpreferencesのメニューにもねぇYO!!

# flutterの開発環境構築をした時に起きた問題

下記のような症状がflutter doctorコマンドを打ったら出現した。ポチポチで行けるって聞いてたけど、どうやら神様は俺にはそうさせてくれないらしい。

```
$ flutter doctor
Android license status unknown.
      Try re-installing or updating your Android SDK Manager.
      See https://developer.android.com/studio/#downloads or visit https://flutter.dev/setup/#android-setup for detailed instructions.
```

詳しく見てみようとしてみて、下記のように出た。

```
$ flutter doctor --android-licenses
Android sdkmanager tool not found (/Users/naoya.ohsaki/Library/Android/sdk/tools/bin/sdkmanager).
Try re-installing or updating your Android SDK,
visit https://flutter.dev/setup/#android-setup for detailed instructions.
```

実際にこの/Library/Android/sdk/tools/bin/sdkmanagerへcdコマンドを使って覗いてみたところ、toolsディレクトリがそっくりそのまま無い状態だった。なんでやねんwww草www

症状としては、下記の方の記事に酷似していた。

[Android sdkmanager tool not found.の解決法](https://qiita.com/masa-321/items/0e0643b9b848f146ef3d)

が、大きく違っていたのは、Android StudioのPreferencesから、Android SDKの設定を開いてみると、SDKToolsというところに表示されているはずのAndroid SDK Toolsがそもそも無いのである。なんで本体インストールした時に一緒になってねーんだって話ですが、致し方なし。

[公式](https://developer.android.com/studio?hl=ja#command-tools)からCommand line tools onlyというところにあるmacOS用のzipファイルをダウンロード。解凍したらtoolsディレクトリが出てきてくれたので、

```
$ mv Downloads/tools /Users/naoya.ohsaki/Library/Android/sdk/
```

これで、Android Studioを再起動したら、Android StudioのPreferencesから、Android SDKの設定を開いてみると、SDKToolsというところにAndroid SDK Toolsが表示されたので、これにチェックを入れてインストールさせました。

さて、これでflutter doctor

```
$ flutter doctor
[!] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
    ! Some Android licenses not accepted.  To resolve this, run: flutter doctor --android-licenses
```

お、ちょっと変わった。flutter doctor --android-licensesを実行してみやがれって言ってるので実行してみます。

```
$ flutter doctor --android-licenses
Warning: File /Users/naoya.ohsaki/.android/repositories.cfg could not be loaded.
6 of 7 SDK package licenses not accepted. 100% Computing updates...
Review licenses that have not been accepted (y/N)? y

1/6: License android-googletv-license:
　　　　・
　　　　・
　　　　・
All SDK package licenses accepted
```

6個ものyを打ち込んでやってpackageライセンスに同意する質疑応答に答えると、

```
$ flutter doctor
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
```

おー、できてるできてる。クリア！！
haproxyにSSL証明書を設置

## 概要
haproxyにSSL証明書を設置する方法。

## 環境
- HA-Proxy version 1.5.18 2016/05/10

## 証明書を用意する
### オレオレ証明書を使う場合
keyファイル（秘密鍵）を作成

```shell-session
$ openssl genrsa 2048 > server.key
```

csr（証明書署名要求）を作成。入力項目は全て空欄でEnterで問題ない。

```shell-session
$ openssl req -new -key server.key > server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
```

crt(拡張子をcerにすることもある)（サーバー証明書）を作成

```shell-session
$ openssl x509 -days 730 -req -signkey server.key < server.csr > server.crt
```

### pemファイルを用意
haproxyでは、SSL証明書はpemファイルにする必要がある。
crtファイルとkeyファイルを結合して拡張子pemとして1つのファイルにするが、以下の順番になっている必要がある。

SSL証明書 -> 中間証明書（ある場合） -> 秘密鍵

```shell-session
$ cat server.crt server.cer server.key > server.pem
```


すると以下のようなpemファイルが出来上がる。(中間証明書がない場合はCERTIFICATEは1つのみになる)

```
-----BEGIN CERTIFICATE-----
xxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
xxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
xxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END RSA PRIVATE KEY-----
```

server.pemは秘密鍵情報を含むので、chmodで適切な権限にすること。

```shell-session
$ sudo chmod 400 server.pem
```

## 設定ファイルを編集
/etc/haproxy/haproxy.cfg

以下の箇所にpemファイルのパスを設定する。

```ini
# http -> https転送
frontend http-in
    bind *:80
    redirect scheme https code 301 if !{ ssl_fc }

# SSL設定
frontend main
    bind    *:443 ssl crt /etc/haproxy/server.pem
    mode    http
    option forwardfor
    reqadd X-Forwarded-Proto:\ https
    default_backend  app
```

haproxyを再起動

```shell-session
$ sudo systemctl restart haproxy
```

## 参考
- [HAProxyでSSL処理をできるようにする](https://www.na3.jp/entry/20131202/p1)
- [オレオレ証明書をopensslで作る（詳細版）](https://ozuma.hatenablog.jp/entry/20130511/1368284304)
- [haproxyでのhttpsリダイレクト設定](https://qiita.com/y_marito/items/1a6b5a1b8806352dc038)
Processingで初めて0から作ったプログラム【まとめ】

どうも私です。
今回は最終回ということで、【その１～３】のまとめをしていきます。

今までの記事
[Processingで初めて0から作ったプログラム【その１】][link1]
[link1]:https://qiita.com/KURIeIGHT/items/9d314f63f310ac05ae04
[Processingで初めて0から作ったプログラム【その２】][link2]
[link2]:https://qiita.com/KURIeIGHT/items/41064edae5e7fd4ffc4f
[Processingで初めて0から作ったプログラム【その３】][link3]
[link3]:https://qiita.com/KURIeIGHT/items/e98b2a5aa515997befed

#プログラムを作り終えて…
**処理の手順**

**1.楽譜の五線を検出**
    1.画像のグレースケール化
    2.画像のエッジ検出
    3.線分の検出
    4.五線の検出

**2.音符の検出**
1.入力画像から検出したいところを指定
2.テンプレートマッチング
3.一番類似度の高かった結果を表示する

**3.音の出力**
1.認識した音符が何の音なのか定義する。
2.「C D E F G A H」の音ファイルをその音符と結びつける
3.音を鳴らす
（この開発はProcessingとOpenCVを使って開発しました。）



　この開発して、まず思ったのは達成感です。
何書かれていない状態から、すべてを作り上げたのですよ！
やっと終わったって感じです。

#まとめ
#####大変だったところ
 Processingで自分のようなことをしている人がいなく、いろいろなテンプレートを少しずつ修正して1つのコードにまとめあげる、というのがとても大変でした...

　この研究を経て**関数**の大切さを知ることができました。
関数に応用するまでの時間がなく、テンプレート画像のみしか対応できなかったですが、範囲を設定して音符の検出もできる関数ができればどんな楽譜でも対応することが可能だったと思います。

また１年後この投稿を見返して、こんな幼稚なことしてたのかと思えるぐらいの力をつけていきたいですね。

おわり。
FormDataでファイルもオブジェクトも一緒に送る方法

#ハマった箇所
axiosでファイルも、オブジェクトも送りたいが、オブジェクトが[object Object]になってしまう。

#なぜ送れなかったのか？
[FormData.append]（https://developer.mozilla.org/ja/docs/Web/API/FormData/append）

> The field's value. This can be a USVString or Blob (including subclasses such as File). If none of these are specified the value is converted to a string.

FormDataに追加するときにValueはUSVStringかBlobにしてね、それ以外だと全部テキストにするよとのこと。

その為直接入れるのはNG

#送る方法
Axiosでファイルを送る方法

```js
storeFiles(reportId) {
        let formData = new FormData()
        //複数送りたいときは配列にしてあげることも可能
        this.inputs.map(file => {
          formData.append('files[]', file)
        })
        //headerを設定する必要あり。
        let config = {headers: {'content-type': 'multipart/form-data'}}
        axios.post('/path/desu', {
          report_id: reportId
        }), formData, config)
          .then(res => {
            console.log(res.data)
          })
      }
```

# Axiosでファイルとオブジェクトを同じFormDataで送る方法

```js
//送りたいオブジェクト
var examples  = {a: 'task', b: {c: 'small_task'}}

storeFiles(reportId) {
        let formData = new FormData()
        //複数送りたいときは配列にしてあげることも可能
        this.inputs.map(file => {
          formData.append('files[]', file)
        })
        //JSON文字列に変換
        let tasks = JSON.stringfy(this.examples)
        formData.append('tasks', tasks)
        //headerを設定する必要あり。
        let config = {headers: {'content-type': 'multipart/form-data'}}
        axios.post('/path/desu', {
          report_id: reportId
        }), formData, config)
          .then(res => {
            console.log(res.data)
          })
      }
```

サーバーサイドでJson文字列をデコードしてあげれば配列としてきちんと扱うことができる。

```php
//laravel
public function store(Request $request, $report_id) 
{
    $data = $request->all();
    $task = json_decode($data['tasks']);
    var_dump($task);
    // ['a' => 'task, 'b' => ['c' => 'small_task']]
}
JavaScriptで定時で動く何かを作る。

備忘録的なもの。async/awaitやアロー関数を使っているので、IEだとお陀仏。書き換えも面倒なのでこのまま。
# 大元のコード
```ruby:JavaScript
window.looper = {
    wait: async function(callback, ms) {
        await new Promise(resolve => setTimeout(resolve, ms));
        return callback();
    },
    time_array: function(min=["00"]) {
        var ary = Array.from(Array(24), (r, i) => i);
        var res = [];
        for(var a of ary) for(var m of min) res.push(a + ":" + m);
        return res;//["0:00","1:00","2:00",...,"23:00"]
    },
    timing_loop: function(callback, time=["0:00"], expire_sec=365*24*60*60) {
        var start = new Date();
        function loop() {
            var t = new Date();
            var m = t.getMinutes();
            m = m < 10 ? "0" + m : m;
            var r = t.getHours() + ":" + m;
            if((expire_sec * 1000) < (t - start)) {
                return console.log("looping expired");//一年経ったらループ終了
            }
            if(-1 < time.indexOf(r)) {
                console.log(r);
                callback();
                return looper.wait(loop, 60000);
            }
            return looper.wait(loop, 500);
        }
        loop();
    }
};
```
# 使い方
```ruby:JavaScript
looper.timing_loop(function() {
    console.log("お昼の時間。");
}, ["12:00"]);

//12:00
//お昼の時間。
```
```ruby:JavaScript
looper.timing_loop(function() {
    console.log("じかんです。りそくがつきます。");
}, looper.time_array(["00", "30"]));

//0:00
//じかんです。りそくがつきます。
//0:30
//じかんです。りそくがつきます。
//1:00
//じかんです。りそくがつきます。
//...
```

「JavaScriptだとコードが長くなるなあ」って書いていて思った。Pythonのリスト内包表記が恋しい。FireFoxでは使えるって小耳にはさんだけど、Chromeじゃ使えないんだって。悲しい。