OmniAICreator/Qiita-1.07M huggingface dataset:
HTML5 <input type="datetime-local"> に現在時刻を設定するには

日付と時刻を設定できるブラウザーネイティブなフォーム`<input　type="datetime-local">`を便利に使う方法をご紹介します。CodePenでデモを作りました。カレンダーアイコンをクリックすると、日付や時刻を設定できます。

<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="wvOXWzd" data-user="takatama" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/takatama/pen/wvOXWzd">
  HTML5 input datetime-local</a> by Hirokazu Takatama (<a href="https://codepen.io/takatama">@takatama</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

このデモでは、Webページを開いたときに現在時刻を設定しています。ソースコードはStackoverflowに掲載しています。

https://stackoverflow.com/questions/24468518/html5-input-datetime-local-default-value-of-today-and-current-time/60884408#60884408

HTMLには`id="cal"`の`<input>`を配置します。

```html
<input type="datetime-local" id="cal">
```

JavaScriptでは`<input>`の`value`に時刻の文字列を`yyyy-MM-ddThh:mm`の形式で設定します。

```javascript
function toLocalISOString(date) {
  const localDate = new Date(date - date.getTimezoneOffset() * 60000); //offset in milliseconds. Credit https://stackoverflow.com/questions/10830357/javascript-toisostring-ignores-timezone-offset

  // Optionally remove second/millisecond if needed
  localDate.setSeconds(null);
  localDate.setMilliseconds(null);
  return localDate.toISOString().slice(0, -1);
}

window.addEventListener("load", () => {
  document.getElementById("cal").value = toLocalISOString(new Date());
});
```

`<input>`に設定する文字列は`toISOString().slice(0, -1)`で最後のZを消すことで生成できるのですが、UTC が返るためタイムゾーンを考慮する必要があります。そこで`getTimezoneOffset()`のずらしますが、分からミリ秒に変換しています。

その後、`setSeconds(null)`とすれば秒を、`setMilliseconds(null)`とすればミリ秒をフォームに出力しないように設定できます。

なお、`value`に設定できる文字列は、次の形式のいずれかです。

- ```yyyy-MM-ddThh:mm```
- ```yyyy-MM-ddThh:mm:ss```
- ```yyyy-MM-ddThh:mm:ss.SSS```

この形式に従わないものを設定しようとすると、開発者コンソールに次のエラーが表示されます。

```console
The specified value "XXX" does not conform to the required format.  The format is "yyyy-MM-ddThh:mm" followed by optional ":ss" or ":ss.SSS".
```

仕様はこちらです。

https://developer.mozilla.org/ja/docs/Web/HTML/Element/Input/datetime-local
【Minecraft 1.12.2】TileEntitySpecialRendererで透過色を利用するために必要なもの

# 方法
私が、最低限必要だと判断した処理です。参考にしたサイトを張っておくので、不安な旗はそちらもご覧ください。Lighting設定は、なくても動くことは動きます。
GlStateManager版（シンプルで分かりやすい）

```java:
GlStateManager.disableLighting();;//不自然なライティングがなくなる
GlStateManager.enableBlend();//奥にあるブロックが真っ白になるのを防ぐ
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GlStateManager.enableDepth();//これがないと透過しない
GlStateManager.depthMask(false);//奥の海面が正しく表示されるようになる

GlStateManager.color();//色を設定（任意）
//表示処理
//設定を元に戻す
GlStateManager.enableDepth();
GlStateManager.depthMask(true);
```

GL11版（古いバージョンにも対応）

```java:
GL11.glDisable(GL11.GL_LIGHTING);//不自然なライティングがなくなる
GL11.glEnable(GL11.GL_BLEND);//奥にあるブロックが真っ白になるのを防ぐ
GL11.glBlendFunc( GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA );
GL11.glEnable(GL11.GL_DEPTH_TEST);//これがないと透過しない
GL11.glDepthMask(false);//奥の海面が正しく表示されるようになる

GL11.glColor4f();//色を設定（任意）
//表示処理
//設定を元に戻す
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glDepthMask(true);
```
参考にしたサイト
https://www.minecraftforge.net/forum/topic/22162-solved17x-tesrs-with-alpha-render-issues/
optuna

このライブラリを一言で表すと、ハイパーパラメータを自動で探索するツールです。
機械学習のモデルのチューニングを手軽にやりたいかたにおすすめの動画になっています。プリファードネットワークはオプチューナを用いてgoogleの物体検出コンペで準優勝しました。では、オプチューナの三つの特徴を説明します。その前にハイパーパラメータとパラメータについて説明します。
　ハイパーパラメータはモデルを学習する前の設定です。それに対してパラーメータは学習中に作られる設定です。ハイパーパラメータを手動で探索する場合、一つ一つ自分で探索しないといけないため、大変時間がかかりるため嫌ですよね。オプチューナを使うとハイパーパラメータを自動で探索することができます。このオプチューナの特徴を三つ紹介します。
　まず、一つ目はデファインバイランです。モデルの作り方にはデファインアンドランとデファインバイランがあります。デファインアンドランはモデルを定義しコンパイルして学習をします。学習は速いですが、学習中にモデルを変えることができません。デファインアンドランはモデルの定義と学習を同時に行います。そのため、学習中にモデルを変更することができます。
 次に、二つ目が枝刈りです。学習の初期段階で評価値が低いものを予測し学習を停止することができます。これによって余計な学習をしなくて済みます。時間の節約になりますね。
 三つ目は分散最適化です。機械学習は計算量が多く時間がかかります。複数のパソコンで処理をすることで計算速度が上がります。
 三つの特徴をまとめるとこうなります。一つ目はデファインバイランです。モデルの定義と学習を同時に行うことができます。二つ目は枝刈りです。学習の初期段階で評価値が低いものを停止します。三つ目は分散最適化です。複数のパソコンで処理を行うことで計算速度が上がります。
Nuxt.js + Vue.js + Vuex によるカウンターのサンプルコード

## 概要

- Nuxt.js + Vuex を使用して「+」「-」ボタンで数値が増減するカウンターを作成する

## 今回の環境

- Node.js 13.12.0
- Nuxt.js 2.12.1

## Nuxt.js + Vuex によるカウンターのサンプルコード

### ソースコード一覧

```
├── package.json
├── pages
│   └── counter.vue
└── store
    └── counter.js
```

### package.json

```json
{
  "name": "my-app",
  "dependencies": {
    "nuxt": "2.12.1"
  }
}
```

### pages/counter.vue

```vue
<template>
  <div>
    <p>{{ count }}</p>
    <p>
      <button @click="increment">+</button>
      <button @click="decrement">-</button>
    </p>
  </div>
</template>

<script>
export default {
  computed: {
    count () {
      console.log('Call the computed count')
      return this.$store.state.counter.count
    }
  },
  methods: {
    // 「+」ボタンクリック時に呼ばれる
    increment () {
      console.log('Call the methods increment')
      // mutations の increment を呼び出す
      this.$store.commit('counter/increment')
    },
    // 「-」ボタンクリック時に呼ばれる
    decrement () {
      console.log('Call the methods decrement')
      // mutations の decrement を呼び出す
      this.$store.commit('counter/decrement')
    }
  }
}
</script>
```

### store/counter.js

```js
// カウンターの値を管理するストア (Vuex.Store)

export const state = () => ({
  count: 0
})

export const mutations = {
  increment (state) {
    console.log('Call the mutations increment')
    state.count++
  },
  decrement (state) {
    console.log('Call the mutations decrement')
    state.count--
  }
}
```

## Node.js サーバを起動

package.json に記述したライブラリをインストール。

```
$ npm install
```

Node.js サーバを起動。

```
$ ./node_modules/nuxt/bin/nuxt.js

   ╭─────────────────────────────────────────────╮
   │                                             │
   │   Nuxt.js v2.12.1                           │
   │   Running in development mode (universal)   │
   │                                             │
   │   Listening on: http://localhost:3000/      │
   │                                             │
   ╰─────────────────────────────────────────────╯

ℹ Preparing project for development
ℹ Initial build may take a while
✔ Builder initialized
✔ Nuxt files generated

✔ Client
  Compiled successfully in 5.69s

✔ Server
  Compiled successfully in 5.30s

ℹ Waiting for file changes
ℹ Memory usage: 121 MB (RSS: 200 MB)
ℹ Listening on: http://localhost:3000/
```

Web ブラウザで http://localhost:3000/counter にアクセスすると「+」「-」ボタンと数値が増減するカウンターが表示される。

## 参考資料

- [インストール \- NuxtJS](https://ja.nuxtjs.org/guide/installation/)
- [Vuex ストア \- NuxtJS](https://ja.nuxtjs.org/guide/vuex-store/)
- [Vue\.js \+ Vuex によるカウンターのサンプルコード \- Qiita](https://qiita.com/niwasawa/items/e8b5f1ef1650537c854a)
多次元リスト(配列)を一次元に直す

空のリスト(配列)を新しく作成し、元の要素を一つずつコピーする。

pythonの場合

```test.py

# coding: utf-8

animal_list = ["イヌ", ["ペルシャ", "マンチカン", "スコティッシュフォールド"], "ヒト", ["ハムスター, カピバラ"]];
new_list = []

for animal in animal_list:
    if type(animal) == list:
        new_list.extend(animal)
    else:
        new_list.append(animal)
        
print(new_list)
```
<br>

js(gas)の場合

```js:test.gs
function make_array() {
 
  var array = ["イヌ", ["ペルシャ", "マンチカン", "スコティッシュフォールド"], "ヒト", ["ハムスター, カピバラ"]];
  var new_array = [];
  
  for (var i = 0; i <= array.length - 1; i++) {
    if (typeof(array[i]) == "object") {
      array[i].map(function(text) { new_array.push(text) });
    } else {
      new_array.push(array[i]);
    }
  }
  
  Logger.log(new_array);

}
```

<br>

<メモ>
・リストは可変(大きさを決める必要がない)に対し、配列は不変。(宣言時に大きさを決める必要がある)
・jsでは配列の大きさを決める必要がない？
Google Places API のデータで営業中か判定するアルゴリズム

[Places API](https://developers.google.com/places/web-service/intro?hl=ja) を使えば、Google Map に登録されているデータを取得できます。
Places API は取得したいデータに応じて API が複数ありますが、Place Details で [fields](https://developers.google.com/places/web-service/details?hl=ja#fields
) に ```opening_hours``` を指定すれば、そのお店の開店・閉店時間を取得できます。

営業時間が次のお店なら、

- 日: 11:30 AM – 2:00 PM, 5:00 PM – 3:00 AM
- 月: 11:30 AM – 2:00 PM, 5:00 PM – 3:00 AM
- 火: 11:30 AM – 2:00 PM, 5:00 PM – 3:00 AM
- 水: 閉店
- 木: 11:30 AM – 2:00 PM, 5:00 PM – 3:00 AM
- 金: 11:30 AM – 2:00 PM, 5:00 PM – 3:00 AM
- 土: 11:30 AM – 2:00 PM, 5:00 PM – 3:00 AM

API で取得できるデータは次の通りです。なかなか複雑ですね！

```json
{
  "periods": [
    {
      "close": {
        "day": 0,
        "time": "1400"
      },
      "open": {
        "day": 0,
        "time": "1130"
      }
    },
    {
      "close": {
        "day": 1,
        "time": "0300"
      },
      "open": {
        "day": 0,
        "time": "1700"
      }
    },
    {
      "close": {
        "day": 1,
        "time": "1400"
      },
      "open": {
        "day": 1,
        "time": "1130"
      }
    },
    {
      "close": {
        "day": 2,
        "time": "0300"
      },
      "open": {
        "day": 1,
        "time": "1700"
      }
    },
    {
      "close": {
        "day": 2,
        "time": "1400"
      },
      "open": {
        "day": 2,
        "time": "1130"
      }
    },
    {
      "close": {
        "day": 3,
        "time": "0300"
      },
      "open": {
        "day": 2,
        "time": "1700"
      }
    },
    {
      "close": {
        "day": 4,
        "time": "1400"
      },
      "open": {
        "day": 4,
        "time": "1130"
      }
    },
    {
      "close": {
        "day": 5,
        "time": "0300"
      },
      "open": {
        "day": 4,
        "time": "1700"
      }
    },
    {
      "close": {
        "day": 5,
        "time": "1400"
      },
      "open": {
        "day": 5,
        "time": "1130"
      }
    },
    {
      "close": {
        "day": 6,
        "time": "0300"
      },
      "open": {
        "day": 5,
        "time": "1700"
      }
    },
    {
      "close": {
        "day": 6,
        "time": "1400"
      },
      "open": {
        "day": 6,
        "time": "1130"
      }
    },
    {
      "close": {
        "day": 0,
        "time": "0300"
      },
      "open": {
        "day": 6,
        "time": "1700"
      }
    }
  ]
}
```

このデータを使って、店舗が営業中かどうかを判定するアルゴリズムは次のようになります。

```javascript

/**
 * 営業中か判定する
 * @param {Date} d 日付オブジェクト
 * @param {object} openingHours 営業時間のデータ
 * @return {boolean} 営業中なら true、閉店なら false
 */
const isOpen = (d, openingHours) => {
  const day = d.getDay();
  const h = String(d.getHours()).padStart(2, '0');
  const m = String(d.getMinutes()).padStart(2, '0');
  const time = h + m;
  return openingHours.periods.filter(p => p.open.day === day || p.close.day === day).some(p => {
    const s = p.open.time;
    const t = p.close.time;
    if (p.open.day === p.close.day) return s <= time && time <= t;
    return (s <= time && time <= '2400') || ('0000' <= time && time <= t);
  });
};
```

コーディング例を https://codepen.io/takatama/pen/vYOvEQP に置きました。
Dataflowの料金が高くなったので安くしてみる

#はじめに
この記事は前回[GCPのほうのIoTCoreを使ってみる](https://qiita.com/Akijin/items/c060a416b9d5e0b52de8)の続きになります。
まずそちらを御覧ください。

会社の方に**Dataflowのマシンタイプは変更できるよ**と教えていただいたので早速変更したところかなり料金をカットすることができたのでまとめます。

#本題
先日の記事の通り今回の運用で一番料金が高くなってしまったのがDataflowになります。
デバイスの台数が多くなればなるほどDataflowは相対的に安くなるのですが今回は1台で運用しているため割高になってしまっています。
どう考えてもテンプレートで作製したジョブはハイパワーすぎるのでこれを小さくして料金を削減していきます。

#たったこれだけ
テンプレートで作成したジョブではおそらくn1-standard-4が標準で使用されていると思います。(明記されているのがなかったので確実ではない)
デバイス1台のセンサデータの処理しかしないのにどう考えてもメモリは15GBもいらないですしこれだけ大きいと料金もかかります。
なので使用するマシンタイプを変更します。

Dataflowではオプションパラメータとしてマシンタイプを変更することができます。
ここではe2-mediumにします。e2-microで作製しようとしたところ**小さすぎてダメだよ**と言われてしまったので最小ではないe2-mediumを使用しています。
また増えることはないと思いますがワーカーの最大を１に設定しておきます。
<img width="622" alt="スクリーンショット 2020-03-27 20.20.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269207/d77b90ef-6c51-8958-163d-5c0576bf6204.png">
あとは前の記事の通りの設定をするだけでテンプレートを使用して料金を抑えることができます。
テンプレートをそのまま使用したときと比べると料金が**約20%**になります。


#さいごに
今回はデバイスの台数が１台という個人で使用する際のユースケースでしたが台数が増えると先述したようにDataflowは割安になります。
そもそもPub/Subまで届いたデータをDataflowを使わずにCloudFunctionsを使用したほうが安く済む場合もあるかと思います。
ご自身の環境や運用方法に合わせたサービスの選定をする用に心がけましょう。
【Googleスプレットシート】QUERY関数/INDIRECT関数で表示行数を動的に指定する方法

GoogleスプレットシートのQUERY関数及びINDIRECT関数を活用する際に、表示行数を動的に指定する方法についての説明です。

#値のある行数だけQUERY関数/INDIRECT関数でデータ表示する方法
**今回の想定**
今回は、例としてGoogleフォームでデータでイベント参加者を募るケースを例として挙げます。

>（前回の記事）
[Googleフォームで取得した画像をスプレットシートで表示する方法【セル内に画像で表示】](https://qiita.com/fuku_up/items/c99a6f711c5caa74961b)

>サンプルデータと、各種結果は[こちら](https://docs.google.com/spreadsheets/d/1pSpe733qB3ztR2ZmNjhom-bPtLLsKwSSD8qLmmOmiV8/edit?usp=sharing)にて公開しておりますので、必要に応じてコピペして使ってください。

Googleフォームで取得した画像URLから、【数式①】Web上で画像を取得するURLを作成する数式と、【数式②】その数式をもとにセル上に画像を表示する数式を作成し、下の行へコピーしたとします。（数式の詳細は、[過去の記事](https://qiita.com/fuku_up/items/c99a6f711c5caa74961b)をご覧ください。）

![query_indirect_get_line_number_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609490/505f9a48-a2d6-159f-aa29-7f153650c967.jpeg)

```:数式①
= CONCATENATE(“http://drive.google.com/uc?export=view&”,QUERY(SPLIT(E2,“?”),“select Col2”))
```

```:数式②
= IMAGE(F2)
```

このように数式をコピーすると、参照元の値がな`れて意図しない空値が表示されてしまうなど弊害が起こる可能性があります。**

なので、正しい値が入っている行（期待する行数）だけに絞ったデータを扱う、シートを新しく作ります。その際に、`QUERY関数`と`INDIRECT関数`を使うのですが、参照元で値が適切に入っている行数を取得して、必要なデータだけ表示するようにします。

なお、画像表示を行う`IMAGE関数`を`QUERY関数`で取得すると画像が表示されない仕様になっているようなので、今回の例では`INDIRECT関数`も併用していますが、`IMAGE関数`の扱いがなければ**`QUERY関数`だけで事足りる**と思います。

#INDIRECT関数で必要行数を表示
![query_indirect_get_line_number_02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609490/c8504d05-60a2-b865-076c-a3501f1841a2.jpeg)

ということで、まずは`INDIRECT関数`を用いた画像の取得です。A1セルに以下の数式を入力しています。

```
= {INDIRECT(“‘作業シート’!G1:G”&COUNTA(‘作業シート’!A:A))}
```

｛ ｝を用いてコピーする配列を作成しているのですが、参照元の範囲指定にINDIRECT関数を活用しています。範囲指定には数式を使って、以下の文字列を作成しています。

>‘作業シート’!G1:G10

参照元のシートからIMAGE関数で画像表示している列（今回の例だとG列目）に関して、*回答者数の数*を指定して範囲を決めています。こ*の回答者数の数*は増減する値ですが、`COUNTA関数`を用いて、Googleフォームが送られると強制的に追加される（＝空値にはならない）カラム（タイムスタンプ）の数を求めてきています。

#QUERY関数で必要行数を表示
次はQUERY関数ですが、B1セルに以下を入力しています。

```
= Query(‘作業シート’!A1:F50,“SELECT B,C,D,A,F LIMIT “&COUNTA(‘作業シート’!A:A)–1)
```

考え方は、`INDIRECT関数`のときと同じです。`QUERY関数`の範囲指定（第一引数）に関しては、参照元シート（今回だと”作業シート”）のA1:F50を指定しています。これは、対象となる値がA~F列に格納されているためです。また、対象とする行数は50行にしていますが、50件以上のデータが入る見込みがある場合は適宜余分を見込んで設定しなおしてみてください。

![query_indirect_get_line_number_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609490/aaa68967-119f-5118-7568-cd9e700c93b5.jpeg)

第２引数では、関数を使って以下のクエリを作成しています。
>SELECT B,C,D,A,F LIMIT 9

“B,C,D,A,F”の部分は、表示したい順に参照元ファイルの列を指定してください。全部取得したい場合は”\*”にしておけばOKです。その後は、クエリで取得する行数を求めていますが、こちらも`INDIRECT関数`のときと同じく、**`COUNTA関数`を用いて回答者の数を取得してきています**。ただ今回は、テーブル名が入っている1行目が余分にカウントされてしまうので、`COUNTA`の値から1を引いています。

---
ということで、Googleスプレットシートの`QUERY関数` / `INDIRECT関数`で表示行数を動的に指定する方法を説明しました。

値の入らない数式など予め省いておくことで、スプレットシートの値を外部サービスから参照する際に予期せぬノイズが入らなかったりと何かと便利だと思うのでおすすめです。
cron-job.orgで定期的にサーバー上の処理を実行

# [cron-jobサイト](https://cron-job.org/en/)
## 使い方
[無料で指定したURLを定期的に自動実行できるサービス「cron-job.org」](https://www.lancork.net/2014/08/cron-job-org/#toc_title_1)

## サーバー上のPHPを実行
### 結果
正常に動作

## 課題
7:05,8:05,9:05分に実行など出来ない

## Firebase Cloud Functionsを実行
### 結果
失敗 下記を参考に実施
【結果】エラーとなる（詳細少々お待ちください。）
kubectlコマンド

# kubectlコマンド

よく使いそうなkubectlコマンドをメモしておきます。

## 基本的なkubectlコマンド

|コマンド|内容|
|:---|:---|
|kubectl create deployment --image=<イメージ名> <デプロイメント名>|イメージ<イメージ名>を<デプロイメント名>で実行する|
|kubectl scale --replicas=<レプリカ数> deployment/<デプロイメント名>|デプロイメント<デプロイメント名>のレプリカを<レプリカ数>に設定する|
|kubectl create job <ジョブ名> --image=<イメージ名>|イメージ<イメージ名>を<ジョブ名>で実行する|
|kubectl run <ポッド名> --image=<イメージ名> -it --restart=Never --rm sh|イメージ<イメージ名>を<ポッド名>で実行する|
|kubectl exec -it <ポッド名> -c <コンテナ名> sh|ポッド<ポッド名>のコンテナ<コンテナ名>を別ターミナルで起動する|
|kubectl delete pod <ポッド名>|ポッド<ポッド名>を削除する|
|kubectl delete deployment <デプロイメント名>|デプロイメント<デプロイメント名>を削除する|
|kubectl delete job <ジョブ名>|ジョブ<ジョブ名>を削除する|
|kubectl logs <ポッド名>|ポッド<ポッド名>のログを表示する|
|kubectl logs <ポッド名> -c <コンテナ名>|ポッド<ポッド名>のコンテナ<コンテナ名>のログを表示する|
|kubectl cluster-info|クラスタ情報を表示する|
|kubectl get node|ノード一覧を表示する|
|kubectl get pods|ポッド一覧を表示する|
|kubectl get pod -o wide|ポッド一覧を表示する|
|kubectl get deployment|デプロイメント一覧を表示する|
|kubectl get jobs|ジョブ一覧を表示する|
|kubectl get componentstatus|コンポーネントの状態を表示する|
|kubectl get all|いろいろ表示する|
|kubectl describe pod <ポッド名>|ポッド<ポッド名>の詳細情報を表示する|
|kubectl version|Kubernetesのバージョンを確認する|
|kubectl expose deployment <デプロイメント名> --port 80 --type LoadBalancer|ポート80で外部ロードバランサを作成する（インターネットに公開する）|
|kubectl get services|サービス一覧を表示する|
|kubectl get replicasets|レプリカを表示する|

## マニフェスト関連のkubectlコマンド

|コマンド|内容|
|:---|:---|
|kubectl apply -f <ファイル名>|マニフェストファイル<ファイル名>から実行する|
|kubectl delete -f <ファイル名>|マニフェストファイル<ファイル名>から精製したオブジェクトを削除する|
【TypeScript】Next.jsでAppバーを共通化する方法

#_app.tsxにAppバーコンポーネントを追加する

以下参考プログラムです。

```TypeScript:_app.tsx
import React from "react";
import App from "next/app";
import Bar from "../components/Bar";

export default class MyApp extends App {
  static async getInitialProps({
    Component,
    ctx
  }: {
    Component: any;
    ctx: any;
  }) {
    let pageProps = {};

    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx);
    }

    return { pageProps };
  }

  render() {
    const { Component, pageProps } = this.props;

    return (
      <React.Fragment>
        <Bar />
        <Component {...pageProps} />
      </React.Fragment>
    );
  }
}


```
小ネタ: macOS 標準のコマンドだけで MacBook を閉じても勝手に寝ないようにする

# TL;DR

* macOS には `caffeinate` というコマンドが標準であって、スリープ突入を一時的に抑制できるよ
* `launchd` というプロセスが macOS では常時動いていて、システム起動のタイミングとかログインのタイミングでプログラムを起動したりしてくれているよ
* `launchd` に `caffeinate` コマンドをユーザーログインのタイミングで起動させたらあとは自動的にスリープ突入が抑制できたよ

# 使い方
## `caffeinate.plist` ファイルを作る

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>caffeinate</string>
    <key>ProgramArguments</key>
    <array>
      <string>/bin/sh</string>
      <string>-c</string>
      <string>exec caffeinate -s -w $$</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
  </dict>
</plist>
```


## `~/Library/LaunchAgents` ディレクトリにコピー

## launchd に認識させる

ターミナルなどで以下のコマンドを実行する。

```shell-session
launchctl load ~/Library/LaunchAgents/caffeinate.plist
```

# 補足
## 適用範囲

この小細工を上記の手順で導入した場合、導入された Mac は導入したユーザーがログインすると AC 電源が繋がっている限り蓋を閉めようが何をしようが勝手にスリープしない状態になる。

ログアウトだけして放置するなどした場合の挙動については未調査。

## 一時停止

この小細工を一時的に無効にして、勝手にスリープする状態に戻すには、以下のコマンドを実行する。

```shell-session
launchctl stop caffeinate
```

再度有効にするには以下のコマンドを実行するか、 Mac を再起動する。

```shell-session
launchctl start caffeinate
```

## アンインストール

この小細工を取り除いて恒久的に向こうにするには、以下のコマンドを実行する。

```shell-session
launchctl unload ~/Library/LaunchAgents/caffeinate.plist
```

caffeinate.plist ファイルは削除してもしなくても良い。

## `~/Library/LaunchAgents` 以外のインストール先

インストール先によって、どのタイミングでスリープ抑制がかかるのかが変化する。
詳しくは launchd.plist(5) を参照。

* `/Library/LaunchAgents`
* `/Library/LaunchDaemons`
* `/System/Library/LaunchAgents` (今日の macOS では勝手にここへファイルを置けない)
* `/System/Library/LaunchDaemons` (同上)

agent と daemon の違いは http://www.maruko2.com/mw/LaunchDaemons_(launchctl,_launchd.plist)_%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9#Daemon_.E3.81.A8_Agent_.E3.81.AE.E9.81.95.E3.81.84 が端的にまとまっている。

# 動作原理

launchd に caffeinate コマンドを自動起動させることで、スリープを抑制する。

caffeinate コマンドのオプションに `-w pid` というものがあるが、これによって pid をプロセス ID に持つプロセスが終了するまでスリープをしないで欲しいとカーネルに要請できる。

ところで、この pid の検査がザルのため、 caffeinate コマンド自身の PID を指定しても問題なくカーネルに受理されてしまい、結果として caffeinate コマンドが自身の終了を待つような動き、すなわち永久にスリープ突入を阻止できるという目標が達成できる。

詳しくは launchd.plist(5) caffeinate(8) を参照。
vagrantとDockerで環境構築をした

## はじめに

[Vagrantを使う「Mac最速のDocker環境」を初心者向けに解説](https://qiita.com/necocoa/items/bd62ed3dba14b17552f2)を参考に環境構築をしたときにエラーとなったことをまとめて書いていきます。

VirtualBoxをインストールから、Mutagenのインストール・セットアップまでは問題なくできたので、その次のコマンドから書いていきます。

## Vagrantを起動
コマンド実行後に、エラーが発生しました。

```zsh
$ vagrant up
/Users/hoge/.vagrant.d/gems/2.4.9/gems/vagrant-mutagen-0.1.2/lib/vagrant-mutagen/Mutagen.rb:22:in `initialize': No such file or directory @ rb_sysopen - /Users/hoge/.ssh/config (Errno::ENOENT)
```

`config`が無いと言われているので、作ってあげます。
ディレクトリ名かと思ってたらファイル名でした

```zsh
$ touch /Users/hoge/.ssh/config
```

再度、upすると今度は成功するはずです。

```zsh
$ vagrant up
```
## 仮想環境にSSH接続
接続はすんなりできました。

```zsh
$ vagrant ssh
```

```zsh
root@087fc4a682c3:/ cd app
root@087fc4a682c3:/app# docker-compose -f docker-compose.dev.yml build
root@087fc4a682c3:/app# docker-compose -f docker-compose.dev.yml up

# dockerにログインしてないとエラーが出たらログインしましょう
root@087fc4a682c3:/app# docker login
```

## さいごに
[Vagrantを使う「Mac最速のDocker環境」を初心者向けに解説](https://qiita.com/necocoa/items/bd62ed3dba14b17552f2)は初心者の私にとってわかりやすかったです。</br>
さらなるエラー箇所については、解決したら追記していきます。
Vue.js + Vuex によるカウンターのサンプルコード

## 概要

- Vue.js + Vuex を使用して「+」「-」ボタンで数値が増減するカウンターを作成する

## 今回の環境

- Vue.js 2.6.1
- Vuex 3.1.3

## Vuex とは

[Vuex とは何か？ \| Vuex](https://vuex.vuejs.org/ja/)

> Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。 これは予測可能な方法によってのみ状態の変異を行うというルールを保証し、アプリケーション内の全てのコンポーネントのための集中型のストアとして機能します。

[ステート \| Vuex](https://vuex.vuejs.org/ja/guide/state.html)

> Vuex は 単一ステートツリー (single state tree) を使います。つまり、この単一なオブジェクトはアプリケーションレベルの状態が全て含まれており、"信頼できる唯一の情報源 (single source of truth)" として機能します。これは、通常、アプリケーションごとに1つしかストアは持たないことを意味します。

[Vuex 入門 \| Vuex](https://vuex.vuejs.org/ja/guide/)

> Vuex アプリケーションの中心にあるものはストアです。"ストア" は、基本的にアプリケーションの 状態（state） を保持するコンテナです。単純なグローバルオブジェクトとの違いが 2つあります。
> 
> 1. Vuex ストアはリアクティブです。Vue コンポーネントがストアから状態を取り出すとき、もしストアの状態が変化したら、ストアはリアクティブかつ効率的に更新を行います。
> 
> 2. ストアの状態を直接変更することはできません。明示的にミューテーションをコミットすることによってのみ、ストアの状態を変更します。これによって、全ての状態の変更について追跡可能な記録を残すことが保証され、ツールでのアプリケーションの動作の理解を助けます。

## Vue.js + Vuex によるカウンターのサンプルコード

以下の HTML を Web ブラウザで読み込むと「+」「-」ボタンと数値が増減するカウンターが表示される。

```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue.js + Vuex ぽちぽちカウンターサンプル</title>
</head>
<body>

<div id="app">
  <p>{{ count }}</p>
  <p>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<script src="https://unpkg.com/vuex@3.1.3/dist/vuex.js"></script>

<script>

// カウンターの値を管理するストア
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    // increment: state => state.count++
    increment() {
      console.log('Call the mutations increment')
      this.state.count++
    },
    // decrement: state => state.count--
    decrement() {
      console.log('Call the mutations decrement')
      this.state.count--
    }
  }
})

new Vue({
  el: '#app',
  computed: {
    count () {
      return store.state.count
    }
  },
  methods: {
    // 「+」ボタンクリック時に呼ばれる
    increment() {
      console.log('Call the methods increment')
      // mutations の increment を呼び出す
      store.commit('increment')
    },
    // 「-」ボタンクリック時に呼ばれる
    decrement() {
      console.log('Call the methods decrement')
      // mutations の decrement を呼び出す
      store.commit('decrement')
    }
  }
})
</script>

</body>
</html>
```

## 参考資料

- [Vuex とは何か？ \| Vuex](https://vuex.vuejs.org/ja/)
デジタルトランスフォーメーション（DX）時代にとるべき EC 事業戦略とは

# EC は D2C の時代に
D2C とは Direct to Consumer の略称で、商品を直接消費者に販売するEC（電子商取引）のことです。近年 D2C に取り組む企業が増えてきています。野村総合研究所（NRI）は[ITナビゲーター2020年版](https://www.nri.com/jp/knowledge/book/lst/2019/o_book213)にて「BtoC EC（消費者向けEC）」の市場規模は2025年度に27兆8000億円に拡大すると予測しています。2019年度から比較すると約1.4倍の成長が続く見通しです。ECの増大や、決済手段としての電子マネーの普及により、企業と顧客との接点がどんどん「デジタル化」されています。

> 　近年、リアル店舗という顧客接点を有することを強みとして小売企業もデジタル化を進めておりますが、ITプラットフォーマーが顧客との接点と情報を握り、小売領域に進出する動きも出始めています。
　この流れに小売企業が対応していくためには、まず自社の有するデータを整備・統合してDX戦略の基盤を構築することが求められます。その上で、自社の強みを磨きつつプラットフォーマーと連携する、さらには自社の強みを小売企業独自のプラットフォームビジネスへと変換するなど、大きな変革が求められます。
　こうしたDXの取り組みを成功させるためには、これまでの営業部門、EC部門、商品部門などの機能別組織に横串を通す機能を設置し、次世代を担う人材をDXリーダーとして起用することなども求められます。こうした変革への労を惜しまず行える企業こそ、小売業界での勝ち組になっていくと予想されます。
出典：[『ITナビゲーター2020年版』――2025年度までの市場トレンド予測](https://www.nri.com/jp/journal/2020/0108)

# D2C 型 EC のメリット
このような市場環境の中、変革が求められる企業は自社で EC サイトを運営するケースが増えています。IT プラットフォーマーを通じた販売では利益率が低く、カスタマージャーニーをコントロール出来ない状況が生まれます。D2C、つまり商品を直接消費者に販売することで得られるメリットには以下の４つがあります。

1. 顧客データを収集する機会が得られる
2. ブランドのロイヤルティの強化
3. 商品のパーソナライズ
4. 新商品のテストマーケティング

D2C に取り組むことは、お客様との直接の対話を生み出す機会の創出と言えます。NRI デジタルでは、クライアントの D2C ビジネスの企画からシステム構築、運営サポートまでを一貫して支援、クライアントの事業拡大に貢献しています。

# D2C を支える EC 技術のトレンド
![ECマトリクス-884x500.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/127260/f4cabfdb-8ead-6745-35c4-23fb6c11c90e.png)

さて、ここからは EC サイトを構築する上で必要となる技術・ソリューションをいくつかご紹介します。わかりやすさのために極端に表現すると小規模でシンプルな EC サイトを始める際にはネットショップを利用し、大規模で既存の物流や会計システムと繋ぐ必要のある複雑な業務を実現する場合はフルスクラッチで開発します。その中間に EC パッケージやオープンソース、カート ASP が位置している状況が従来の EC サイト構築サービスの構図でした。

しかし、ここ数年で SaaS と言われるジャンルへ多くのサービスがシフトしています。パッケージのように大規模システムにも対応でき、カートASPのような定期的な機能追加などの柔軟性も併せ持つ形態です。オープンソースの老舗である EC-CUBE も共創型クラウド EC プラットフォーム [ec-cube.co](https://www.ec-cube.net/product/co/) をリリース、世界最大の EC プラットフォームの [Shopify](https://www.shopify.jp/?ref=nri-digital-ltd) が日本へ上陸する等、今後もこのトレンドは続くものと考えられます。

# これからの EC 事業戦略
お客様に自社のブランドを知ってもらい、根強いファンになってもらうためにはお客様との直接の対話を生み出す機会を増やすことが大切です。そのチャネルの一つが EC サイトです。技術の進化によりこれまでよりも手軽に EC サイトを構築・運用することが可能になりました。

事業全体のデジタルマーケティングを考える上で EC のあり方を議論して頂ければと思います。次回以降は EC サイト構築を取り巻く各種ソリューションについて深堀りをしていきます。

<本記事に関するお問い合わせ>
NRIデジタル株式会社　担当　吉田・倉澤・萩村 and Developers　marketing-analytics-team@nri-digital.jp

> 本記事は NRI digital TECH BLOG からの転載です。
> [デジタルトランスフォーメーション（DX）時代にとるべき EC 事業戦略とは](https://www.nri-digital.jp/tech/20200323-1641/)
Raspberry Pi 4にRaspbian(OS)をインストールする for mac

3とかでも一緒です。備忘録

## 1. SDカード初期化

- macにSDカードを差し込む
- `$ diskutil list` で該当のSDカードを確認（今回はdisk2）
- `$ sudo diskutil eraseDisk FAT32 RASP MBRFormat /dev/disk2` でSDカードをDOS-FAT32に初期化

## 2. Raspbian(OS)のイメージをSDカードにコピー
- [公式サイト](https://www.raspberrypi.org/downloads/raspbian/)からRaspbianをダウンロードする
- zipファイルを解凍する
- `$ sudo diskutil unmountDisk /dev/disk2` でSDカードをアンマウントする
- `$ sudo dd bs=1m if=/path/to/2020-02-13-raspbian-buster.img of=/dev/rdisk2` でRaspbianイメージを書き込む
- ddコマンドの途中経過は `Ctrl + t` で確認できます。
- 注意点として、ddコマンドに時間がかかる場合(5分以上とか)はzipファイルの解凍方法がよくないようです。その場合、ラズパイの公式サイトは[こちら](https://theunarchiver.com/)を使ってzipファイルを解凍することを推奨しています。
- *wifi接続やOTGモードにする場合はこの段階で追加作業が発生します* （例：[Raspberry Pi 4をOTGモードでsshする方法(+raspi-config経由でwifi接続する) for mac](https://qiita.com/aykusu/items/a44cbf49b0c24fa1b3f8)）
- 全てが終わったら `$ sudo diskutil eject /dev/rdisk2`してSDカードをmacから抜く
- ラズパイにmicroSDを差し込む

終わり
今更ながらPuppeteerのVersionが2.1になって変わったこと

# はじめに
Puppeteerのメジャーバーションが2020/01/28に2.1にアップデートされました。すでに二ヶ月が経過し、Playwrightという新たなライブラリも登場しましたが、この記事では主な変更点について書いています。

# Puppeteerとは
Node.jsでHeadless Chromeを操作するライブラリです。Chrome DevTools（開発者ツール）を使って操作しています。
GoogleChromeチームが作成・公開しています。

# リリースノート
変更点はここに書いてあるので、公式を読む場合はこちらから
https://github.com/puppeteer/puppeteer/releases/tag/v2.1.0

# 変更点
## 公式のFirefoxがサポートされました。（ただしまだ実験的な機能）
実はv2.1より前から、PuppeteerでFirefoxを操作する機能は実験的に組み込まれており、Puppeteerのソースコードの中で `puppeteer-firefox` というライブラリが存在していました。そしてこれは、npmパッケージとしても公開されています。
https://www.npmjs.com/package/puppeteer-firefox
※現在はdeprecatedになっています。

PuppeteerはChrome DevTools Protocol（CDP）を使って、Chromeを操作しています。FirefoxにはCDPは存在しないのにどうやって操作しているかというと、Puppeteerの開発チームによって `Juggler` という、Firefoxのリモートデバッグツールを開発しサポートして再現していました。
https://github.com/puppeteer/juggler
※現在はdeprecatedになっています。

これは、Firefoxにカスタムパッチを当てて実現していました。つまり、Mozilla公式から提供されているFirefoxではありませんでした。
Puppeteer v2.1では、このJugglerは使われていません。Puppeteerの開発チームとMozillaが協議した結果、Firefoxにカスタムパッチを当てて開発を継続することは困難であると判断され、Mozillaと `stock` をサポートしてもらいMozillaと協力して開発を行っています。
※stockは公開されていないみたいです。調べても出てこなかった。

Puppeteer開発チームは、Safariのような別のブラウザベンダが開発しているブラウザにPuppeteerをサポートするために協力、クロスブラウザコマンドを実行するための調査を続けていくようです。

といった内容が、READMEのこの部分に追加されています。
https://github.com/puppeteer/puppeteer#q-what-is-the-status-of-cross-browser-support

あと、Chroniumのバージョンが80になりました

## まとめ
Playwrightも複数のブラウザに対応していますが、Puppeteerも今後対応を続けていく予定で、かつブラウザベンダが提供しているブラウザで動作するよう（WebkitではなくSafariといった）活動を続けていくようです。Playwrightとの棲み分けが進んでいくかもしれないです。
どちらが生き残るかではなく、どちらにも注目して使っていくことになるんじゃないかと予想しています。
Pythonで毎日AtCoder #18

### はじめに
[前回](https://qiita.com/taxfree_python/items/35d8b35ba12022c8d594#comment-d815527acb3c71beec86)
今日も類題です。数理の翼二日目

### #18
**考えたこと**
[ABC095-A](https://atcoder.jp/contests/abc095/tasks/abc095_a)
カウントしてから*100するだけ

```python
s = str(input())
print(700+s.count('o')*100)
```
[ABC085-A](https://atcoder.jp/contests/abc085/tasks/abc085_a)
sの4番目からが出力と共通の部分なのでスライスで保持。それに2019を連結しています。

```python
s = str(input())
print('2018'+s[4:])
```

[ABC069-B](https://atcoder.jp/contests/abc069/tasks/abc069_b)
len(s)から最初と最後の2文字を引いて解くだけ

```python
s = str(input())
print(s[0]+str(len(s)-2)+s[-1])
```

[ABC082-B](https://atcoder.jp/contests/abc082/tasks/abc082_b)
1WA。文字列のsortとかが苦手
文字列のsortを忘れていたので、ordで数字に変換してsortしていました。辞書順の差を最大化するためにはsはa→z順、tはz→aにする。あとは比較するだけ。

```python
s = str(input())
t = str(input())

s = list(map(ord,s))
t = list(map(ord,t))
s.sort()
t.sort(reverse=True)
if s < t:
    print('Yes')
else:
    print('No')
```


### まとめ
数理の翼で感化されたので、量子コンピュータの方もがんばる。では、また
【MySQL】再インストールしても、諸々エラーが発生する時に確認して欲しいこと。

#はじめに
mysql8→5.7に切り替える際に、

```
.. ERROR! The server quit without updating PID file (/usr/local/var/mysql/MacBookPro.local.pid).
```
やたらとこのエラーに時間を使ったので、書き残しておきます。

#結論
プロセスを確認して、それを殺す。

```console
$ ps aux | grep mysql
```
実行後、僕の場合だと、mysql8が残ってました。
なのでそれを

```console
$ kill -9 （プロセス番号）
```
これで削除。

細かいことは、よくわかりませんが、裏で前のバージョンが動いていたから、それを削除したら使えるようになるってことですかね。
ssh設定変更

#不正アクセス対策
１日のアクセスログチェック
$cat /var/log/secure | grep "Received disconnect" | wc -l
としたところ3767件のアクセスを確認。
ユーザ名,portはroot,22に限らずランダムな模様。

ひとまずrootアクセスを不可に。
$ vi /etc/ssh/sshd_config
PermitRootLogin yes →PermitRootLogin no

- sshのポートを変更
sshのサービスファイルからポート番号変更
/usr*のファイルを直接変更せず/etcに編集ファイルをコピー
$cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/ssh.xml
 -  <port protocol="tcp" port="22"/>
 +  <port protocol="tcp" port="1234"/>
ファイアーウォール更新
$ firewall-cmd --reload

sshdのポート設定変更
$ vi /etc/ssh/sshd_config
 #Port22
↓
Port1234
$ systemctl restart sshd

これで不正アクセスは大幅に減ったと思われる
ーーーーーーーーーーーーーーーーー
- ※macのターミナルがよく固まるのでmac側で対策
---- vi追記 ----
Host *
ServerAliveInterval 15
fsrplを使う


[fsrpl](https://github.com/matsu0228/fsrpl)

## 概要
Cloud Firestoreのデータのダンプ、リストアとかできるツール　便利

## 利用環境の構築

### インストール
homebrew を信じろ

``` console
$ brew tap matsu0228/homebrew-fsrpl
$ brew install fsrpl
```

### サービスアカウントから秘密鍵をダウンロード
https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk?authuser=0

- firebase -> プロジェクトを設定 -> サービスアカウント
- Firebase Admin SDKの「新しい秘密鍵を生成」を押下


### 秘密鍵を環境変数に追加

コマンドの基本利用方法はオプションに秘密鍵へのパスを指定するらしいが、面倒なので登録を推奨

```console
$ export FIRESTORE_SECRET=生成した秘密鍵への絶対パス
```

ぼくは複数環境扱う関係でこうしました。

```sh
alias dev="export FIRESTORE_SECRET=~/.fsrpl/dev_secret_key.json"
alias stg="export FIRESTORE_SECRET=~/.fsrpl/stg_secret_key.json"
alias prd="export FIRESTORE_SECRET=~/.fsrpl/prd_secret_key.json"
```

## 利用方法

### ダンプ

ダンプするとドキュメント単位で ${DOCUMENT_ID}.json で保存される

#### 単一ドキュメントをダンプ

```console
$ fsrpl "${COLLECTION_NAME}/${DOCUMENT_ID}" -f ${LOCAL_DUMP_PATH}
```

#### コレクション内の全ドキュメントをダンプ

```console
$ fsrpl "${COLLECTION_NAME}/*" -f ${LOCAL_DUMP_PATH}
```


### リストア

※この際 ${DOCUMENT_ID} を指定すれば指定したID、*の場合はファイル名をIDとする

#### 単一ファイルをリストア

```console
$ fsrpl "${COLLECTION_NAME}/${DOCUMENT_ID}" -i ${RESTORE_FILE}
or
$ fsrpl "${COLLECTION_NAME}/*" -i ${RESTORE_FILE_PATH}
```

#### 複数ファイルをリストア

```console
$ fsrpl "${COLLECTION_NAME}/*" -i ${RESTORE_FILES_PATH}
```

## 同一環境内でコピー

##### 同一コレクションにドキュメントIDを変えてコピー

```console
$ fsrpl "${COLLECTION_NAME}/${DOCUMENT_ID}" --destPath="${COLLECTION_NAME}/${OTHER_DOCUMENT_ID}"
```

##### 別のコレクションに同一ドキュメントIDでコピー

```console
$ fsrpl "${COLLECTION_NAME}/${DOCUMENT_ID}" --destPath="${OTHER_COLLECTION_NAME}/*"
```

#### 別のコレクションに別のドキュメントIDでコピー

```console
$ fsrpl "${COLLECTION_NAME}/${DOCUMENT_ID}" --destPath="${OTHER_COLLECTION_NAME}/${OTHER_DOCUMENT_ID}"
```

#### 別のコレクションにコレクションを丸ごとコピー

```console
$ fsrpl "${COLLECTION_NAME}/*" --destPath="${OTHER_COLLECTION_NAME}/*"
```

### 別の環境にコピー

#### 単一ドキュメントをコピー

```console
$ fsrpl "${COLLECTION_NAME}/${DOCUMENT_ID}" --destfs --dest-secret="${SECRET_KEY_FULLPATH}" --destPath="${COLLECTION_NAME}/*"
```

#### コレクションをコピー

```console
$ fsrpl "${COLLECTION_NAME}/*" --destfs --dest-secret="${SECRET_KEY_FULLPATH}" --destPath="${COLLECTION_NAME}/*"
```

便宜上同じコレクション名でコピーしているが、コピー先のコレクション名を変えることも可能

`--destPath="*/*"` としてしまうとそれぞれの `${DOCUMENT_ID}` のコレクションができてしまうので注意


### できないこと

- 参照フィールドを別環境にコピー

全ての参照しているフィールドが参照元のドキュメントIDを指すような参照に置き換わってしまう
`projects/${SRC_PROJECT_ID}/databases/(default)/documents/${SRC_REF}/${SRC_DOCUMENT_ID}`


参考: 
https://qiita.com/mtskhs/items/65e66caa6dc9cd706c76
【Ruby】getsメソッドの入力値は改行付きの文字列である

# 背景
某転職テストのスキルチェックを受けた結果がボロボロで悔しくなり、某スキルチェックサイトで毎日コーディングして訓練しようと思い始めた所、あまりにも単純な問題で詰まり悲しみに包まれかけたのでアウトプットします。

また、Railsエンジニア志望なのでRubyのコードを書いていきます。

どことは書いてはないですがスキルチェックの内容は外部流出厳禁なので、一応似たような形に書き換えてます。

# getsメソッド
### getsメソッドはユーザーがキーボードで入力した値を文字列として取得するメソッド。
という説明自体は間違っていないのですが、

```ruby:sample.rb
# 入力した値が"Hello"だった場合、”World"を出力しましょう。
input = gets #Helloを入力する
if input == "Hello"
  puts "World"
else
  puts "Helloではありません"
end
> "Helloではありません"
```

のように、間違いなくHelloを入力しているのにfalseが返ってくるんですね。えぇー！？って思いますやん。
調べた所、getsメソッドは正確に言うと、

### 入力した値の末尾に改行が付与された文字列として取得するメソッド
となります。

```:ターミナル(irb)
irb(main):001:0> input = gets
Hello
=> "Hello\n" #末尾に\nが付く
```

マジか…:sob:
このように1分で解けそうな問題なのに原因が分からず、結局50分掛かりました。ぐやじい〜〜

と言うことで、今回の問題のソリューションはこれ。

```ruby:sample.rb
# 入力した値が"Hello"だった場合、”World"を出力しましょう。
input = gets.chomp # ここー！！！！
if input == "Hello"
  puts "World"
else
  puts "Helloではありません"
end
> "World"
```

chompを付けることで改行をなくせるんですね。
基礎中の基礎とはいえ普段使わないメソッドだから勉強になりました:hugging:
【Linux】PATHについて

ターミナルのコマンドである`PATH`について記載していきます。
#PATHとは
`コマンド検索パス`を追加するコマンドです。
`コマンド検索パス`とは、コマンドの実行ファイルを探しにいくようなパスのことです。

```
$ ls
/bin/ls
```
例えば上記のようにフォルダとファイルを参照する`$ ls`を実行したときに、`/bin`ディレクトリの下に`ls`という同名の実行ファイルがあるとします。
`$ ls`はパスを指定せずにコマンドを実行しているので、シェルが各ディレクトリからコマンドに対応している実行ファイルを探しだします。

この探しだすパスを`コマンド検索パス`といい、`PATH`コマンドでこのパスを追加することができます。
##コマンド検索PATHの確認方法
`$ echo $PATH`で確認可能です。ある環境だと下記のように表示されます。

```
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/sbin 
```
パスは`:`で区切られており
`/usr/local/bin`、`/usr/bin`、`/bin`、`/usr/sbin`、`/sbin`、`/usr/local/sbin `の６つが`探索コマンドパス`として設定されています。
##コマンド実行ファイルの格納場所の確認方法
`$ which ls`で確認できます。実行結果は下記になります。

```
$ which ls

#実行結果
/bin/ls
```
##同名の実行ファイルが複数の探索コマンドパスにあるとき
この場合は優先度があり、`$ echo $PATH`で出力された左のものから実行されます。
例えば下記のような場合だと

```
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/sbin
```
`/usr/local/bin`から実行されます。
優先度としては`/usr/local/bin` ＞ `/usr/bin` ＞ `/bin` ＞ `/usr/sbin` ＞ `/sbin` ＞ `/usr/local/sbin `となります。

#PATHの追加
PATHを追加するには`.bashrc`か`.bash_profile`ファイルに`export PATH=$PATH:追加したいコマンド検索パス`形式で記載すると追加されます。

##実際にPATHを追加して通してみる

```:.bashrc
export PATH=$PATH:追加したいコマンド検索パス
```

```:.bash_profile
export PATH=$PATH:追加したいコマンド検索パス
```
PATHを記載するファイルは`.bashrc`と`.bash_profile`のどちらでも問題ありません。

```
$ source ~/.bashrc
$ source ~/.bash_profile
```
それぞれ記述したファイルをsourceコマンドで実行しないとパスが通りません。
##exportコマンドとは
環境変数を表示や設定をする。
###環境変数の表示

```
export -p

#出力結果(一部のみ記載)
declare -x PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
declare -x LANG="ja_JP.UTF-8"
declare -x SHELL="/bin/bash"
```
設定されている環境変数を表示しています。
###環境変数の設定
例えば`$ ULB`という環境変数を設定する場合は、下記のように設定します。

```
$ export ULB=/usr/local/bin

$ echo $ULB
/usr/local/bin #出力結果
```
`$ echo $ULB`で設定されていることを確認していています。

```
ls /usr/local/bin
ls $ULB
```
どちらのコマンドでも同様の結果になります。

###環境変数の上書き

```
$ echo $ULB
/usr/local/bin #出力結果

$ export ULB=/u/l/b

$ echo $ULB
/u/l/b #出力結果
```
上記のように環境変数の上書きを行うこともできます。
###環境変数の削除
`unset`コマンドで削除することができます。

```
$ unset ULB #[$]は入力しなくて良い。

$ echo $ULB
#何も出力されない。
```
###追加するコマンド探索パスの優先度を高くする

```
$ export PATH=追加したいコマンド検索パス:$PATH
```
コマンド探索パスは左から優先度が高くなっているので、このように記載する。

#参考URL
Linux入門 ~「パスを通す」とは~
https://qiita.com/Naggi-Goishi/items/2c49ea50602ea80bf015
Sign in with Apple JSのSafariとそれ以外のブラウザの挙動の違い

# はじめに
Sign in with Apple JSの動作を試している中で気づいた、Safariとそれ以外のブラウザ（Chromeなど）の挙動の違いについて結構ハマったので記事に残します。
同じ問題で困っている人の助けになれば幸いです。
SafariはMacにしか搭載されていないはずなので、この記事はMacOSが対象になります。

# 知っておくべきこと
Safariは任意のパラメータの値が間違っていても、Safariが良い感じに調整してくれて動作するので、WebでSign in With Appleのボタンを設置する場合の動作確認は（動作対象であれば）Chromeでもやった方が良いです。

# 背景
Sign in with Appleのパラメータを確認するためにWebで簡易ログイン画面を作成し、動作確認をしていました。Safariだと成功しますが、Chromeだと `invalid_request` と以下のエラーメッセージが表示され、どのパラメータに問題があるのかがわからない状態で右往左往してました。
`Application is not authorized to access the requested information.`
<img width="1045" alt="スクリーンショット 2020-03-23 18.27.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184103/f9088d2e-c153-bae1-2b42-08dfac538fb0.png">

### 余談: WebでSign in with Appleを使うために必要な手順
以下のブログが一番わかりやすかったです。heckがどういう意味なのかはわかっていない
https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple

https://developer.apple.com でSign in With Appleを使うためにServiceIDを登録する必要があるのですが、ServiceIDを登録するためにはAppIDを登録し、そこでSign in With Appleを有効化して、さらにKeyを登録する必要があります。
公式のドキュメントにもよく読むと記載があるのですが、別ページに分かれておりJSだけで実装したい人でもちゃんとアプリの方も読む必要があります。自分はハマりました。

Sign in With Apple for the webのドキュメント
https://help.apple.com/developer-account/?lang=en#/dev1c0e25352

実際には一つ上のこれも読むとちゃんと書いてある
https://help.apple.com/developer-account/?lang=en#/dev77c875b7e

ドキュメントは全部読みましょう

# 挙動の違い
## Safari
`Sign in With Apple` ボタンをクリックするとポップアップが表示され、パスワードなりtouch barなりでログインを行います。任意のパラメータのvalidateは行われず、ログイン成功すると `usePopup` のパラメータに従いリダイレクトページに遷移するかそのままかの処理が行われます。
（失敗させて遷移させる方法は思いつきませんでした）
また、 `usePopup` の値にかかわらずログインはポップアップで行われます。なのでログインが完了するまでは別の画面に遷移することはありません。

## Chrome
`Sign in With Apple` ボタンをクリックするとログインページにパラメータを全て付与して遷移します。その際、invalidなパラメータがあるとエラー画面が表示されます。
`usePopup` の値に従い、trueの場合は別ウィンドウを表示してログインを行い、falseの場合は元ページをログイン画面に遷移させ、ログイン処理を行います。ログイン完了後にリダイレクトするのはどちらも同じです。

### ちなみにエラーの原因
単純なケアレスミスで、 `scope` パラメータで `name` と `email` の両方を取得したかったのでスペースで区切って指定していましたが、HTMLで出力するときにエンコードされていて、そのパラメータを送っていたことが原因でした。

# 必須パラメータと任意パラメータ
https://developer.apple.com/documentation/signinwithapplejs/incorporating_sign_in_with_apple_into_other_platforms

# 最後に
パラメータミスっても動作するSafari（実際にはしてなかった）はさすがのサポートですね。
今ではもう `scope` パラメータを指定すれば `name` と `email` は取得できるので、まだAppleが対応していないとは思わないようにしましょう。
ベンチャー企業のインターンの面接を受けた話

先日初めてインターンの面接をしたときの話

#　面接した企業
名前は伏せるが、~~（載せていいか調べるの面倒）~~ 医療 × ITのベンチャー企業のインターンを受けた。機械学習やディープラーニングを武器に、医療データを扱う企業だ。
まぁベンチャーのレベル感は大体同じだろうと思うので（適当）今後の参考に。


※当方は専門でない分野の大学4年生で、独学でpythonを勉強している。最近ようやく機械学習のモデルにぶち込めるようになった。まぁそんなにレベルは高くない。

#　面接の内容、聞かれたこと
 - 自己紹介
 - 研究内容
 - 企業に入ってからのビジョン
 - 事前課題の良くできたところ、悪いところ
 - 力量チェック
 - 質問
 - 自己PR

これで1時間面接してたのやばくね

####　自己紹介
まぁこれはインターンやりたい人ならまず考えることだろうけど。参考までに自分のをざっくり。
所属、バイト、研究室について少しずつ話した。
自分はアルバイトでスターバックスで働いていたのだが、IT系（特にベンチャー）はコーヒー好きが多いのでコーヒーの話で盛り上がれるらしい。
好きなものって職種で似そうだから、暇なら学んでもいいかも。
研究室は、内容について後で話すため、自己紹介では研究タイトル程度に。

####　研究内容
自分の研究内容を発表するのと同じ要領で。ただ研究室の同僚よりその分野に精通していないのは確実であるため、できる限り簡単に説明する。改めて自分の研究分野って言葉で説明するの難しいなぁ…
わかりやすく説明することはほんとに重要。
学会とかの経験はあったら若干有利になりそう。

####　企業に入ってからのビジョン
入ってからどんな仕事を想定しているか。
これは企業に対して真剣に働くつもりがあるかのチェックっぽい。
正直めちゃくちゃ頓珍漢なこと言ったけど、頑張ってそうゆうのもできます！って伝えた。

####　事前課題の良くできたところ、悪いところ
事前に機械学習の課題を提出していたのだが、自分は完全に忘れていた（オイ）。
普通に全く想定していなかったので、かなり苦しいことを言ってしまった。
確かになんで課題は提出して終わりだと思っていたのか…反省
良いところ：コメントアウト。褒められた。
悪いところ：特徴量の追加すればよかったって言ったけど、どんなの足すって聞かれたんだが忘れていた。アホ。

####　力量チェック
技術的な質問をされた。機械学習手法のアルゴリズムについて。
線形回帰とかの本当に簡単なやつなら理解できるけどそれでも説明は上手にできない…
ここが一番厳しかった。ビジネスでは他の人に説明することも多いから重要なんだろうなぁ。
もう少し勉強するときに意識しないといけないと感じた。

####　質問
企業に対しての質問。
インターンしながら論文は書けるか聞いた。ほんとはもっと用意してたのに先に説明された笑
5個くらいは用意した方がいい。

####　自己PR
力量でズタボロだったので、学ぶことに対する意欲についてＰＲしたけど…苦しいだろうなぁ。
ただ受動的でなく能動的に動くっていうのは評価されるところ。コンペに出ようと思ってるとか、自分で何か作るとか。

#　感想
自分の勉強不足をひしひしと感じた。データ前処理がうまくなって調子乗ってたけど、全然モデルに関して話せなかった。正直ここで落とされるのは仕方ない。
ただ能動的な行動は評価高いのは感じたので、自分で勉強する姿勢とかをうまく伝えられてれば、何とか…
今後はkaggleのコンペに出るつもりだったけど、それぞれの手法について深堀りしたほうがいいのか…
個人的にモデルのアルゴリズムより前処理のほうが得意だからそっちを推せば良かったなあ
赤外線距離センサ使ってみた

# 概要
赤外線距離センサを購入したので試してみました。
物体からの距離を計測するセンサーです。

# 部品
**距離センサー:**[赤外線距離センサモジュール](https://ja.aliexpress.com/item/32321964595.html?spm=a2g0s.9042311.0.0.27424c4dU6R1mt)
43円
<img width="300" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89227/dbc6b54a-dc22-27ce-9a40-530309e22a24.png">

**表示機:**[TM1637が組み込まれた7セグLED](https://ja.aliexpress.com/item/4000174141889.html?spm=a2g0s.9042311.0.0.27424c4dboG22l)
73円
<img width="300" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89227/4ce296b8-61c4-8857-770e-6173b76eb6bc.png">

**コンピュータ:**[Arduino UNO互換機](https://www.amazon.co.jp/FC-%E5%B7%A5%E5%A0%B4-R3%E9%96%8B%E7%99%BA%E3%83%9C%E3%83%BC%E3%83%89-%E3%83%90%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3-ATmega328P-Arduino%E3%81%A8%E4%BA%92%E6%8F%9B%E6%80%A7/dp/B07H4KTVK5/ref=sxbs_sxwds-stvp?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=2S335VL3VWB52&cv_ct_cx=arduino+uno&keywords=arduino+uno&pd_rd_i=B07H4KTVK5&pd_rd_r=d31aaf3a-0258-4866-b883-ee3cb37525ae&pd_rd_w=II02q&pd_rd_wg=kzB7i&pf_rd_p=e29507e3-7937-4495-82f4-55b5d5ca8047&pf_rd_r=SN5PWYGF5TQS3EX5XFVB&psc=1&qid=1584128141&s=diy&sprefix=Ard%2Cdiy%2C251)
699円
<img width="400" alt="img.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89227/47f88a1f-c4ae-cc1d-84cc-12b8cc736b9b.png">


#配線
<img width="400" alt="スクリーンショット 2020-03-26 7.52.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89227/6cf3fa76-f345-5f57-b0d0-e0ffada5a1d7.png">



#ソースコード

アナログ信号を受け取り、１０回の平均を表示するようにしています。

```c++


#include <Arduino.h>
#include <Wire.h>
#include <TM1637Display.h>
#define SERIAL_BAUD 115200

#define CLK 2
#define DIO 3
TM1637Display display(CLK, DIO);
#define IR_LEN 10
int IRs[IR_LEN] = {0,0,0,0,0,0,0,0,0,0};
int IRs_idx = 0;

void setup() {
  Serial.begin(SERIAL_BAUD);
  while(!Serial) {}

  uint8_t data[] = { 0xff, 0xff, 0xff, 0xff };
  display.setBrightness(0x0f);
  display.setSegments(data);
  delay(1000);
}

void loop() {
  int ave = 0;
  int IR;
  IRs_idx++;
  if(! (IR_LEN > IRs_idx) )
    IRs_idx = 0;
  IRs[IRs_idx] = analogRead(A0);
  for(int i=0 ; i<IR_LEN;i++){
    ave+=IRs[i];
  }
  IR = ave/IR_LEN;

  display.showNumberDec(IR, false); 
  Serial.print("IR: ");
  Serial.print(IR);
  delay(100);
}
```
[github](https://github.com/hashito/arduino_startup/tree/master/IR_Infrared_Obstacle_Avoidance_Sensor)

# 少し感想
初期設定だと`5cm`から`6cm`程度の範囲で検知できます。
その範囲以外だとある`1000`や`25`などの最大/最小値になってしまうため、検知できません。
上部にある可変抵抗をまわすことにより調整が可能です。

# 動作確認
gif動画で上げても分かりにくかったので、Youtubeにて確認いただけると幸いです。
[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/cnrt0CK34uY/0.jpg)](http://www.youtube.com/watch?v=cnrt0CK34uY)
MVCモデル

#MVCモデル
###M...Model(モデル)
Javaクラス
アプリケーションの処理(計算処理等)、データ格納
###V...View(ビュー)
JPSファイル
画面表示
###C...Controller(コントローラー)
サーブレットクラス
要求の受け取り、処理実行依頼(Model)、結果表示依頼(View)

##転送処理

###フォワード
処理を他のサーブレットクラス、JPSクラスに移す(内部)
URLがリクエスト時の状態

```php:記述
RequestDispatcher dispatcher = request.getRequestDispatcher("フォワード先");
dispatcher.forward(request, response);
```

###リダイレクト
処理を他のサーブレットクラス、JPSクラスに移す(外部)
URLがリダイレクト先のものに変更

```php:記述
response.sendReddirect("リダイレクト先のURL");
```
Pythonのargparseでファイルオブジェクトを引数とするオプションでのファイル上書き確認

# 動機

`python`の標準ライブラリにふくまれるコマンドライン・パーサー[argparse](https://docs.python.org/ja/2.7/library/argparse.html)では、引数の型としてファイルオブジェクトを指定することができる。(`argparse.FileType`) 文字列からわざわざファイルオブジェクトを生成したりする必要がないので大変便利である。

しかし、書き込みモード(`mode='w'`)を指定した場合でも有無を言わさず自動的に開いてしまうので、おっちょこちょいな人にとっては、意図せずにファイルを上書きして内容を消してしまう恐れがあり、大変危険である。そこで、ファイルの上書き確認を実装したので、やり方をわすれないようにメモしておく。

# 実装例。

文末に挙げたサイトのやり方を参考にして拙作[pdf_merge_multipages](https://qiita.com/Nanigashi_Uji/items/532c1076a530439fef9a)に実装した例。

```python:pdf_merge_multipages.py(抜粋)

class FileTypeWithCheck(argparse.FileType):

    def __call__(self, string):
        if string and "w" in self._mode:
            if os.path.exists(string):
                sys.stderr.write(('File: "%s" exists. Is it OK to overwrite? [y/n] : ') % (string))
                ans = sys.stdin.readline().rstrip()
                ypttrn = re.compile(r'^y(es)?$', re.I)
                m = ypttrn.match(ans)
                if not m:
                    sys.stderr.write("Stop file overwriting.\n")
                    sys.exit(1)
                    # raise ValueError('Stop file overwriting')
            if os.path.dirname(string):
                os.makedirs(os.path.dirname(string),
                            exist_ok=True)
        return super(FileTypeWithCheck, self).__call__(string)

    def __repr__(self):
        return super(FileTypeWithCheck, self).__repr__()

....

....

def main():
    argpsr = argparse.ArgumentParser(description='Merge multiple mages in PDF files w/o gap.')
    argpsr.add_argument('inputs', metavar='input-file', type=argparse.FileType('rb'),
                        nargs='+', help='Input PDF file(s)')
    argpsr.add_argument('-output', metavar='filename', type=FileTypeWithCheck('wb'),
                        nargs=1, help='Output file', dest='output', default='a.out.pdf')

...
...

def main():
    argpsr = argparse.ArgumentParser(description='Merge multiple mages in PDF files w/o gap.')
    argpsr.add_argument('inputs', metavar='input-file', type=argparse.FileType('rb'),
                        nargs='+', help='Input PDF file(s)')
    argpsr.add_argument('-output', metavar='filename', type=FileTypeWithCheck('wb'),
                        nargs=1, help='Output file', dest='output', default='a.out.pdf')

...
...

if __name__ == '__main__':
    main()
```

ファイルが存在していて上書きを回避したときに、そこでスクリプトを終了(`exit()`)するかもしくは例外を送出するかは、場合によって選択が必要であろう。上記の例では`exit()`しているが、先に`inputs`の引数で読み込みモードでファイルが開かれていた場合に、それを閉じる処理をせずにスクリプトが終了することになる可能性が残るところがちょっと引っかかる点ではある。


## 参考資料

  - https://pod.hatenablog.com/entry/2018/08/17/214654
Vueのディレクトリの責務について考える（2）

[Vueのディレクトリの責務について考える - Qiita](https://qiita.com/kitanote/items/4d5687b6e873e68d5bfd)の続きです。

## ロジックを追加する場所

Vueで業務ロジックを追加する場合、以下の4通りの構成が思い浮かびました。

- SFCのメソッド内に直接書く
- Vuexを使う
- [vuex-orm](https://github.com/vuex-orm/vuex-orm)を使う
- モデルを作成する

### SFCのメソッド内に直接書く

前回まではこのように書いていましたが、コンポーネント間で同じロジックが書かれたりすることにより保守が大変になります。それを避けるためにutil関数とかを作ったりしても用途不明の関数が大量生産され残念なことに。

（小規模なアプリならこの書き方で問題ないです。）

### Vuexで頑張る

複雑な業務ロジックがない場合はVuexでいいかもしれません。（APIを取得してフィルターをかけるだけのような）
ただVuexに依存することになるので、他のFWに変更するとなった場合のコストが大きいです。

### vuex-ormを使う

https://github.com/vuex-orm/vuex-orm

バックエンドのORMのような使用心地、メソッドチェーンでクエリーが記述できるのですごく使いやすいです。（Laravelのormにそっくり）
Vuexの上にさらにライブラリを使うのでもうVueと添い遂げる覚悟が必要そうですね。

### モデルを作成する

この方法が現状では一番いいと思っています。Vuexと併せて使うことも可能です。AngularやReactや将来出る新しいFWに変更する場合もスムーズに移行できると思います。
以下の資料が分かりやすいので、ぜひ読んでみてください。

[Vue.jsで考えるMVVM](https://speakerdeck.com/44x1carbon/vue-dot-jsdekao-erumvvm)

## モデルの例

QiitaItemモデルの作成

``` models/QiitaItem.ts
export default class QiitaItem {
  constructor(
    public body: string,
    public created_at: string,
    public title: string,
    public url: string,
  ) {}

  shortTitle(): string {
    return this.title.substr(0, 20) + '...';
  }
}
```

（TypeScriptのクラスではコンストラクターの引数にアクセス修飾子を付与することで、プロパティの生成と初期化を行なってくれる。）

## Repositoryからモデルを返す

```  repositories/qiitaRepository.ts
import axios from 'axios';
import config from '@/config';
import QiitaItem from '@/models/QiitaItem'

const baseURL = 'https://qiita.com/api/v2/';

const Repository = axios.create({
  baseURL,
  headers: {
    Authorization: 'Bearer ' + config.qiita.token,
  },
});

export default {
  async getItemsBy(userId: number): Promise<QiitaItem[]> {
    let qiitaItems = [];
    const { data } = await Repository.get(`users/${userId}/items`);
    foreach (qiitaItem of data) {
      qiitaItems.push(new QiitaItem(qiitaItem));
    }
    return qiitaItems;
  },
};
```

コンポーネントからはRespositoryの関数を実行するとModelが返ってくる。（配列のモデル）

``` Xxx.vue
import qiitaRepository from '@/repositories/qiitaRepository'
...
const qiitaItems = qiitaRepository.getItemsBy(123);
qiitaItems[0].shortTitle();
```

## ここまでのディレクトリ構成

```
├── components // ステートフルコンポーネント、ステートレスコンポーネント
├── containers // コンテナコンポーネント
├── repositories // API通信
├── models // モデル達
├── views // ページと1:1
├── App.vue // トップページのコンポーネント
├── main.js // プラグインの使用など
├── router.js // ルーティング
└── store.js // Vuex
```

シンプルな業務ロジックであれば、repositoriesとmodelsで完結するのですが、複数のドメインが絡み合うロジックについてはservicesに定義します。（次回に続く？）