OmniAICreator/Qiita-1.07M huggingface dataset:
Array<object>→CSV→ダウンロードをJavaScriptで

[こちらの記事](https://qiita.com/mildsummer/items/25c196632090b08065df)で作った管理画面につけたCSVダウンロードボタンの実装方法についての覚え書きです。
JavaScriptでCSV変換・読み込みを行うライブラリは複数ありますが、カスタマイズ性を考えフルスクラッチで書いてみます。

```javascript
{ id: 1, name: "Mary", words: ["foo", "bar", "baz"] }
```

このように単純なkey-valueを持ったオブジェクトの配列を、以下のようにヘッダー行を持ったCSVに変換します。

```
id,name,words
1,"Mary","["foo","bar","baz"]"
.
.
.
```

```javascript:arrayToCsv.js
/**
 * 単純なオブジェクトの配列をCSV文字列に変換
 * @param {Array<object>} array
 * @param {Array<string>} keyArray
 * @returns {string}
 */
export default (array, keyArray = []) => {
  const keys = keyArray;
  array.forEach((data) => {
    Object.keys(data).forEach((key) => {
      if (keys.indexOf(key) === -1) {
        keys.push(key);
      }
    });
  });
  const lineArray = [keys.join(',')].concat(
    array.map((data) => keys.map((key) => {
      let valueString = '';
      if (typeof data[key] === 'number') {
        valueString = data[key];
      } else if (typeof data[key] === 'string') {
        valueString = `"${data[key]}"`;
      } else {
        valueString = `"${JSON.stringify(data[key]).replace(/"/g, '""')}"`;
      }
      return valueString;
    }).join(',')),
  );
  return lineArray.join('\r\n');
};
```

CSVの構造への変化のために、最初に全てのkeyを自動でリストアップしてから各行の値を入れていきます。
引数にデフォルトのkeyの配列を渡すことでkeyの順番を指定できるようにしました。

この関数は文字列を返すのみなので、文字列からファイルを作成してダウンロードするための関数も用意します。

```javascript:downloadStringFile.js
/**
 * 文字列からファイルをダウンロード
 * @param {string} string
 * @param {string} fileName
 * @param {string} type
 * @param {boolean} insertTimestampToFilename
 */
export default (string, fileName, type, insertTimestampToFilename) => {
  const downLoadLink = document.createElement('a');
  let name = fileName;
  if (insertTimestampToFilename) {
    const nameArray = name.split('.');
    const timestamp = new Date().toLocaleString().replace(/[/ :]/g, '_');
    nameArray[0] = `${nameArray[0]}_${timestamp}`;
    name = nameArray.join('.');
  }
  downLoadLink.download = name;
  downLoadLink.href = URL.createObjectURL(new Blob([string], { type }));
  downLoadLink.dataset.downloadurl = [type, downLoadLink.download, downLoadLink.href].join(':');
  downLoadLink.click();
};
```

ブラウザ上でユーザーにダウンロードさせるためには、aタグの`download`属性を利用します。
`insertTimestampToFilename`にtrueを渡すと、ファイル名にタイムスタンプが追加されます。

実際に使うときはこんな感じです。

```javascript
const csvContent = arrayToCsv(docs.map((doc) => (doc.data())), ['id']);
downloadStringFile(csvContent, 'members.csv', 'text/csv', true);
```

ここでは、Firestoreの`DocumentSnapshot`の配列を単純なオブジェクトの配列に変換したものを`arrayToCsv`に渡し（`id`を行の先頭にする）、タイムスタンプをつけたファイル名でダウンロードさせています。
エラー：pyenv: no such command `sh-activate'


# エラー

```bash
pyenv: no such command `sh-activate'
```

# 解決策

```bash
brew install pyenv-virtualenv
```
# 環境
- pyenv 1.2.15
- Mac  
  ProductName:    Mac OS X  
  ProductVersion: 10.15.1  
  BuildVersion:   19B88
curlでDeploymentをscale

Kubernetes APIをcurlで叩いてdeploymentをスケールさせる手順です。
(curlで実行するくらいならkubectl使ったほうが良いかもしれませんが、何かの言語でリクエストプログラムを作成する際に参考になれば幸いです)
スケールはPATCHメソッドで実行します。

##スケール対象の確認

まず対象deploymentの名前とnamespaceを確認します。

```
kubectl get deploy -n default
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   1/1     1            1           119m
```

環境変数に格納しておきます

```
export DEPLOYMENT=nginx-deployment
export NAMESPACE=default
```

Kubernetes APIサーバーのエンドポイントと証明書のパスも併せて環境変数に格納します。
(エンドポイントの確認方法は[こちら](https://qiita.com/sota0113/items/42b8d2cd53190c9c5bb7#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%BC%E6%83%85%E5%A0%B1%E5%8F%96%E5%BE%97)を参照してください。)
[クラスター情報取得](https://qiita.com/sota0113/items/42b8d2cd53190c9c5bb7#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%BC%E6%83%85%E5%A0%B1%E5%8F%96%E5%BE%97)

```
export CRTFILE=/Users/sota-n/.minikube/ca.crt
export APISERVER=https://192.168.99.100:8443
```

##パッチの準備

適用するパッチを用意します。

```
export PATCH='{"spec":{"replicas":2}}'
```


##パッチの適用(スケール)

curlを実行します。
注意点としては、`Content-Type:`ヘッダーのvalueが `application/strategic-merge-patch+json'`となっています。

```
curl -X PATCH \
-H 'Accept: application/json' \
-H 'Content-Type: application/strategic-merge-patch+json' \
-H "Authorization: Bearer ${TOKEN}" \
"${APISERVER}/apis/apps/v1/namespaces/${NAMESPACE}/deployments/${DEPLOYMENT}/scale" -d ${PATCH} --cacert ${CRTFILE}
```

結果を確認します。

```
kubectl get deploy -n default
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   2/2     2            2           128m
```

以上です。
いろんなサービスと連携できるWebサービス「IFTTT」

Twitter、Facebook、Instagram、Lineなど、多彩なSNSが存在し、
Dropbox、Google Docsなどのストレージサービスなど、
洗い出したらキリがないほどWebサービスが存在しています。

サイト運営をお手伝いしている者としては、
顧客の思いに答えて、様々なWebサービスと連携していく必要があります。

そのWebサービスとの連携は、多くの場合は各サービスが提供しているAPIや、プログラミングが必要になり
全部を網羅するには、かなりの手間と時間とお金がかかる。

しかし、「IFTTT」というサービスを使うことで、
このようなWebサービスたちを簡単に連携することができるのです。

#「IFTTT」とは

各サービスを連携するための、プログラミングの知識は不要です。
既にIFTTTで提供されているサービスで自分の希望と合致していれば使うことができます。
その場合、アクセス許可、URLなどの情報を入力するだけで設定は終わりです。

希望に合致するものがなくても、自分が使いたいWebサービスを選んで、
新たにサービスを作成することができます。

ちなみにIFTTT内ではサービスをAppletというみたいです。
以下、サービスをAppletに置き換えます。

IFTTTは、基本無料で利用できる。
（ただ、一部の月額の利用サービスがあります。）

#「IFTTT」使ってみた

「サイトに記事を投稿した際、Facebookページにも連携したいんだけど、、、」と、
顧客から要望が上がってきました。

Facebookでは、Facebookページ連携するためにAPIを使用しなくてはならず、
使用するには申請及び、審査が必要となります。（もちろんプログラミングして組み込みも）
参考：[審査 - Facebookログイン - ドキュメンテーション - Facebook for Developers
](https://developers.facebook.com/docs/facebook-login/permissions/review?locale=ja_JP
)

申請して、審査完了するまでには数週間必要で、
そもそも申請は、英文で書きます。

そんな時、「IFTTT」というサービスがあることを知って、使ってみることにしました。

今回は、投稿記事をRSSに書き出し、
RSS Feedが更新されたタイミングで、Facebookに連携してみます。

1．アカウントを登録。
　 google、Facebook のアカウントでも登録できます。
　 https://ifttt.com/

2．ヘッダーのExplore をクリック。
3．Searchから「RSS」と「Facebook」を入力して検索。
　 今回の連携に合致するものがあるか見てみる。
4．合致するものがあったので、今回は以下を使ってみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540410/e588bb0b-9be4-4686-8806-0c3ccfc0ec83.png)
サイトから引用

5．Appletを選択して、Settingsを選択。
6．設定画面から、Feed URLに今回使用するRSS URLを入力してSave をクリック、
7．人物アイコンから、My servicesをクリック。
8．Facebookを選択して、Settingsを選択。
9．掲載するFacebookページを選択。
　 その際にFeacbookページの編集権限を持っていることが前提。
10．人物アイコンから、My Appletsを選択。
11．My Appletsから、今回選択したAppletを選択。
12．Connect する。

操作は、以上になります。

リストを上げると、やることが多くありそうですが、
所要時間は、30分もかかりません。

あとは、RSS Feed が更新されて、
Facebookページに掲載されるのを待つだけです。

#「IFTTT」使ってみた感想

思ってた以上に簡単だ！と感じました。
また、Appletという既存で使えるサービスも多く、自分で作るまでなく、探せばある。

プログラミングも必要なく、便利で時短で効率的なサービスなので、
顧客からご要望が合った際に使ってみてはどうでしょうか。。。
【FileMaker】ちょっとしたヘルプメッセージを出す

画面を作っていると、「このボタンはこういう役目があるんだよ」ということをテキストでボタンの下に書くのがちょっとレイアウト的に場所がなかったり格好悪かったりします。
そんな時のヘルプメッセージの出し方として、ポップオーバーを利用しています。

※ポップオーバーは、ver.13以降で実装可能です。

どういうことかというと、こんな感じでボタンなどメッセージを表示したい箇所の側の「？」をクリックすると。。。
<img width="324" alt="スクリーンショット 2019-12-05 18.00.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60538/ef3faf90-f52c-edeb-1639-d5738dba69e1.png">

ヘルプメッセージが出てきます。
<img width="632" alt="スクリーンショット 2019-12-05 18.00.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60538/0b4aa552-7110-cf18-13ce-4ffbb19c7fc9.png">

#実装
では、実装の仕方はどうなってるかと言うと。
いつも通りにポップオーバーをレイアウトに置きます。
メッセージは、タイトルバーに書き込みます。
<img width="416" alt="スクリーンショット 2019-12-05 18.00.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60538/ede13786-1777-477d-2c5d-74b395b57526.png">
これはいつものポップオーバーですね。
が、ちょっと違うのは、ボタンの形をしていない、ということです。

この設定は、いたって単純です。
ボタンのアクション**全て**(通常、ポイントした時に表示、押したとき、フォーカス）において、塗りつぶし色と枠線の色を「指定なし」にします。
<img width="263" alt="スクリーンショット 2019-12-05 18.00.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60538/a5a6c5e7-8fdf-8cf1-d1fd-a1bd642acacd.png">
<img width="250" alt="スクリーンショット 2019-12-05 18.01.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60538/4ec88943-1d3a-e73b-7472-7bee0798f629.png">
こうすることで、ぽかっと「？」アイコンだけが出ているように見えます。
ボタンの枠線があると「あれ？このボタンは押してもいいところなのかな？」となることが多いですが、「？」アイコンだけだと「これはヘルプなんだな」と認識されます（される可能性が大きいです）。

#ボタンの枠線があると。。。
ちなみにボタンに枠線があると、こんな感じにちょっとどころじゃないかっこ悪さが出てしまいます。
<img width="327" alt="スクリーンショット 2019-12-05 18.20.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60538/5b254aa4-db7e-c9b3-d30e-9509cde868b2.png">

また、「画像を使えば解決？」ということもあるかもしれませんが、すでに標準で実装されているアイコンですので統一感が取れるというのと、手軽にぽろっと出てくるポップオーバーの使い道って様々だなーと思うので、少ない手持ち（少ない技術）で実装ができます。
あれこれ覚えるよりは、断然ラクです。

さりげなーくヘルプを実装する時には、こんなポップオーバーの設定も使ってみて下さい。
Arduino IDEでbad CPU type in executableというエラーが出る解決策



久々にArduino IDEを開いたら、以下のエラーで書き込めなくなっていた。

```
fork/exec /Applications/Arduino.app/Contents/Java/tools-builder/ctags/5.8-arduino11/ctags:
bad CPU type in executableボードESP32 Dev Moduleに対するコンパイル時にエラーが発生しました。
```

# 動作環境
macOS Catalina (10.15.1)
Arduino.app (1.8.9)

# 解決策
どうやら新しいMac OSでは64bitのアプリケーションしか動作しないらしく、32bitであった以前のバージョンは開なくなったという。

公式より最新版の1.8.10をインストールすることで解決した。
https://www.arduino.cc/en/Main/Software

# 参考
https://forum.arduino.cc/index.php?topic=620175.0
nodenv-yarn-install プラグインが上手く動かなかった時の対応メモ

Node.js な環境が必要なとき、自分の開発環境では anyenv 配下の nodenv を使っており、パッケージマネージャーは [npm](https://www.npmjs.com/) を使っていました。

とくに不便を感じたわけではないのですが、[yarn](https://yarnpkg.com/lang/ja/) を使う必要がでてきたため、手元の環境へ yarn をインストールする方法から調べ始めたところ、[nodenv-yarn-install](https://github.com/pine/nodenv-yarn-install) という nodenv のプラグインがよさそうだったので使ってみることにしました。

設定方法も使い方も簡単なようで、すぐにプラグインのインストールは完了しました。

ところが。

nodenv で新しいバージョンをインストールしてみても、既存バージョンを再インストールしなおしてみても、yarn がインストールされない状況に遭遇したので、そのときの解決方法をメモしておきます。

# 最初に結論

自分の場合は

```
% touch $(nodenv root)/default-packages
```

とすることで、プラグインが無事動くようになりました。

# 症状

マニュアル通り nodenv-yarn-install を以下のようにインストールし

```
% mkdir -p "$(nodenv root)/plugins"
% git clone https://github.com/pine/nodenv-yarn-install.git "$(nodenv root)/plugins/nodenv-yarn-install"
```

手元では未インストールだった v12.13.0 をインストールしてみましたが、以下のような感じで、yarn のインストールが始まらず終了してしまいました。

```
% nodenv install 12.13.0
Downloading node-v12.13.0-linux-x64.tar.gz...
-> https://nodejs.org/dist/v12.13.0/node-v12.13.0-linux-x64.tar.gz
Installing node-v12.13.0-linux-x64...
Installed node-v12.13.0-linux-x64 to /Users/.centos/.anyenv/envs/nodenv/versions/12.13.0
nodenv: default-packages file not found
```

`nodenv-default-packages` プラグインが何か言っている……と気にしつつも、この時点で v12.13.0 自体はインストールできており、使える状態になっていました。

ちなみに `nodenv install` コマンドのフックを確認すると以下のように `nodenv-default-packages` と `nodenv-yarn-install` プラグインのトリガーが登録されていました。

```
% nodenv hooks install
/Users/.centos/.anyenv/envs/nodenv/plugins/nodenv-default-packages/etc/nodenv.d/install/default-packages.bash
/Users/.centos/.anyenv/envs/nodenv/plugins/nodenv-yarn-install/etc/nodenv.d/install/yarn-install.bash
```

# 原因

nodenv を使い始めたときに、[nodenv-default-packages](https://github.com/nodenv/nodenv-default-packages) というプラグインをインストールしていました。

このプラグインは `nodenv install` コマンド実行時に自動的にインストールしたい npm パッケージを設定しておけるという便利なものなのですが、インストールはしたものの、自動的にインストールしておきたいパッケージも特になく、マニュアルの**Installation**だけ行った状態で nodenv を利用していました。

マニュアルの**Usage**には自動的にインストールしたいパッケージの書き方および設定ファイルについて記載がありますが、まだなにも必要なかったため、`$(nodenv root)/default-packages` ファイル自体作っていませんでした。

そのため `nodenv install` コマンド実行時に設定ファイルが存在しないとエラーで止まってしまい、 `nodenv-yarn-install` のトリガーが呼ばれず終わってしまっているということのようです。

初期設定を最後までやっていなかった自分が悪いです……

# おまけ

`nodenv-yarn-install` プラグインの実態は

```
"$DEFINITION" nodenv-exec npm install yarn -g --silent 
```

と npm パッケージとして yarn をインストールしているようなので、`nodenv-default-packages` に yarn を登録しておくことと同じかもしれません。

また　[yarn公式サイトのインストールガイド](https://yarnpkg.com/ja/docs/install#alternatives-rc) のページで、 npm でのインストールはあまりオススメしていないような雰囲気があります。

> 注意: npm から Yarn をインストールすることは一般的にはお勧めしません。 Node ベースのパッケージマネージャで Yarn をインストールする場合は、パッケージは署名されておらず、整合性のチェックはベーシックな SHA1 ハッシュのみで行われており、システム全体にまたがるアプリケーションをインストールする場合にはセキュリティリスクとなります。
>
> これらの理由から、使用中の OS に最も適した方法で Yarn をインストールすることを強くお勧めします。

yarn からすると npm は競合ツールなので、これを理由に yarn への乗り換えをお勧めしているのかもしれませんが、公式サイトのインストール方法も確認して、納得したうえで使うのが良いと思います。
iOSのWebViewのUITestで全選択する

## 概要
iOSのXCUITestで、WKWebViewのTextFieldで「すべての選択」できない問題への対処です。

通常の`app.textFields`でアクセスできる要素は情報がありますが、
`app.webViews`以下への対応方法が見つからなかったのでいろいろ試してみました。

普通のTextFiledをtap()したときと違って、webViewだとカーソル位置が中央にくるのもタチが悪い 😇

「すべて選択 -> 削除」の動作を実現しています。

## 環境
- Xcode9以上
- iOS13対応
- WKWebView対応

```swift
// TextFieldをとってくる
let textField = app.webViews.textFields.element(boundBy: num)

textField().tap()
textField().twoFingerTap()
textField().tap()
app.menuItems["すべてを選択"].tap()
textField().typeText(XCUIKeyboardKey.delete.rawValue)
```

これでどうだろうか。。。

## 補足

- プロジェクトの言語設定が日本語のときは、`すべてを選択`でOK
Project設定 -> TARGETS -> Info -> Custom iOS target Properties -> Localization native development region -> Japan

- 英語環境のときは`Select All`でOK
- Simulatorの言語設定は関係ないはずです。
【Kotlin】関数の引数に付与するアノテーションを作る/付与したアノテーションを取得する

やることはタイトルの通りです。

# パラメータに付与するアノテーションを作る
実際のコードは以下の通りです。
`Target`は`VALUE_PARAMETER`を指定します.

```kotlin:アノテーションクラス
@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class Hoge(val value: String)
```

付与する場合以下のようになります。
コンストラクタに対して付与する場合、`param`を頭に付ける必要があります。

```kotlin:コンストラクタのパラメータに付与する例
data class Fuga(@param:Hoge("value") val fuga: String)
```

# 付与したアノテーションを取得する
先ほどの`Fuga`クラスのコンストラクタのパラメータから付与したアノテーションを取得するコードは以下の通りです。

```kotlin
// Hogeのクラスからコンストラクタを取得
val kClazz: KClass<Fuga> = Fuga::class
val constructor: KFunction<Fuga> = kClazz.primaryConstructor!!

// コンストラクタのパラメータからHogeを取り出し
val parameters: List<KParameter> = constructor.parameters
val annotated: List<Hoge> = parameters
        .mapNotNull { param ->
            // Contractは効かないのでHogeとして取り出す場合はキャストが必要
            param.annotations.find { it is Hoge }?.let { it as Hoge }
        }

// 当然値も取り出せる
val values = annotated.map{ it.value }
```
GCP Pub/SubのメッセージはCloud Functionsへどう届くか

# 概要
GCP Pub/SubをTriggerにしたCloud Functionは、メッセージをどう受け取るのか調べてみました。

# 実験
試しに次のようにメッセージをPublishしてみます。

---

<img width="525" alt="Screen Shot 2562-12-05 at 17.47.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74964/da2817eb-ea93-d96e-4ea7-a7e17acf48c7.png">

---

すると、次のようになりました。
`attributes`にメッセージ属性が入り、`data`にメッセージ本文が入ってくるようです。

```ts
exports.test = (pubSubEvent, context) => {
  console.log(pubSubEvent)
};

// -> {"@type":"type.googleapis.com/google.pubsub.v1.PubsubMessage","attributes":{"key":"value"},"data":"bWVzc2FnZQ=="}
```

しかしメッセージ本文はbase64で格納されているため、使える形にするにはデコードする必要があります。

> The payload of the PubsubMessage object, the data published to the topic, is stored as a base64-encoded string in the data attribute of the PubsubMessage. To extract the payload of the PubsubMessage object, you may need to decode the data attribute as shown in the examples below.

ref) https://cloud.google.com/functions/docs/calling/pubsub#event_structure

```ts
exports.test = (pubSubEvent, context, callback) => {
  console.log(Buffer.from(pubSubEvent.data, 'base64').toString())
};

// -> message
```

## Cloud Schedulerの場合
Cloud SchedulerでPub/SubをPublishするとき、payloadを指定できます。
<img width="518" alt="Screen Shot 2562-12-05 at 18.50.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74964/de924bab-2f5a-cdf8-09f8-30cbad097ddc.png">

---

このpayloadは、`data`の中に入ってくるようです。
attributesを指定するには、gcloudコマンド経由でジョブを作成すると良いようです。

ref) [Cloud Schedulerのジョブにattributesを設定する](https://qiita.com/nanakenashi/items/bec9b1b47fe51b4b3981)

## Context
ちなみにcontextの中身は、[こちら](https://cloud.google.com/functions/docs/writing/background#function_parameters)に記載されていました。
TypeScriptで書くとこんな感じ

```ts
type Context = {
    eventId: string,
    timestamp: string,
    eventType: string,
    resource: string
}
```
デザイナーが新規サービスをやってみて先に検証点を決める大切さを学んだ話

[くふうカンパニーAdvent Calendar 2019](https://qiita.com/advent-calendar/2019/kufu)の6日目！

こんにちは、くふうカンパニーの[株式会社Da Vinci Studio](https://da-vinci-studio.com/)に所属している、デザイナーの[@sai_fan_3](https://twitter.com/sai_fan_3)です。
最近は、グループ内のデザインリニューアルや、グループ外のUIデザイン改修をしています。

プロジェクトを進めていくとき（とくに0→1開発）に、検証点を最初に決めるって当たり前では？と思われるかもですが、できてませんでした。検証点を1番初期に計画することの大事さを実感したので、書いてみます。

## Wish
去年携わった新規事業というのが、「スマホ(Web)で結婚式をする」がコンセプトの[Wish](https://www.mwed.jp/wish/)というサービスです。

（くふうカンパニーのグループ内に株式会社みんなのウェディングおよび株式会社Da Vinci Studioがあり、新規事業は、みんなのウェディングのデザイナーとしてやったサービスになります。）

<img width="300" alt="www.mwed.jp_wish_(iPhone X).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545877/692da96a-21cc-b737-b85e-57288b2a9e01.png">

(実は、写真のお知らせにあるように、最近諸事情によってクローズしてしまいました。いいコンセプトだったと思うので、またいつか...)

## 検証点を先に考えればよかった！と実感したこと
リリース時に、 `カップルが結婚を報告できる` ・ `ゲストがお祝いできる` の2点をコア機能として、プロダクトを作成していました。

そんなある日のmtg、リリース時の検証ポイントはどこなのか。という話になり、、、考えられていなかったことに気づきました。
そのときに、一番検証したい項目として上がってきたのが、「Wishのコンセプトが受け入れられるか、ニーズがあるか」という点でした。
そこを検証するためだとしたら、プロダクトを作ること以外にも方法があったのかも🤔と、気づきました。

## 先に検証点を考えることの3つのメリット

### 手戻りがない
検証点を先に決めておくことで、検証結果が間違っていたときにムダになってしまう工数を削減できる。

### 次が見える
検証点をかんがえるときには、「その先で何を決定するため」かを考えるので、プロジェクトの次の段階が見えるようになる。

### 手段が凝り固まらない
プロダクトを作ってからも、もちろん検証できるのですが、プロトタイピングと同じで、モノを作ってしまうと愛着が湧いて、視野が狭くなって手段が限定されていってしまうので、その前に検証をしていくことが大事そう。

## Wishの場合
「Wishのコンセプト（スマホで結婚式をする）が受け入れられるか、ニーズがあるか」という点を検証しようとしていましたが、これを検証するなら、コンセプトが当てはまらなかったらやめる、ということになります。けど、このコンセプトには良いという自信がメンバーにはあったので、ユーザーに感じてもらうには、どういう手段が一番適切なのかをもっと検証する必要があったんだなと思いました。

## 終わりに
「検証して、結果をみて、自信をもって次を進める」その循環を回していけるように、検証をうまく使っていきたいと思います。
JavaScriptで配列をディープコピーしたい

# はじめに
フロントエンドエンジニア1年目の新人プログラマーです。

以下、自分の勉強と備忘録に記録しておきます。参考になれば幸いです。

# 困ったこと

JavaScriptの配列をコピーするだけならいろいろな方法がありますが、
私が困ったのは以下のケースです。

```js
const array1 = [{a:1, b:2}, {a:10,b:12}]
```
array1は維持したまま、各要素のbだけを変更したい

これを単純に値渡しすると、うまくいきません。

```js
const array2 = array1.concat()

array2.forEach(x => x.b = null)
console.log(array2)
// (2) [{…}, {…}]
 0: {a: 1, b: null}
 1: {a: 10, b: null}

console.log(array1)
// (2) [{…}, {…}]
 0: {a: 1, b: null}
 1: {a: 10, b: null}
```

どうやら配列[] 自体は値コピーできていても、各要素は同じ参照になっているのでうまくいかないそうです。

# 解決策
なので各要素も値コピーするために、次の実装が良さそうです。

```js
const array2 = array1.map(x => {
                const y = {};
                Object.assign(y,x);
                return y;});

array2.forEach(x => x.b = null)
console.log(array2)
// (2) [{…}, {…}]
 0: {a: 1, b: null}
 1: {a: 10, b: null}

console.log(array1)
// (2) [{…}, {…}]
 0: {a: 1, b: 2}
 1: {a: 10, b: 12}
```

# まとめ

言語によっては仕様が違うかもしれませんが、javaScriptにおいては上記の実装で解決できました。
PHPのバージョンを7.4にアップデートする

# 課題
PHPのバージョンをアップデートする

## 現状のバージョンを確認する
下記のコマンドでバージョンを確認

```:ターミナル
$ php -v
PHP 7.1.30 (cli)
```

＊ちなみにですが今回はHomebrewを使ってアップデートしようと思います
Homebrewのインストールがまだという方はこちらをソッと置いておきます
(https://brew.sh/index_ja.html)

## PHP7系を検索する
次にHomebrewコマンドを使って現在インストールができるPHPのバージョンを検索します

```:ターミナル
$ brew search php@7
==> Formulae
php@7.2                    php@7.3                    php@7.4
```

## PHP 7.4 をインストールしてみる
どのバージョンをインストールするか決めたら
PHP 7.4を Homebrew経由でインストールします

```:ターミナル
$ brew install php@7.4
```

## PATHを通そう
インストールは無事にされたと思いますが、まだ終わりではありません
PATHを通す必要があるので下記コマンドにて設定を上書きします

```:ターミナル
$ echo 'export PATH="/usr/local/opt/php@7.4/bin:$PATH"' >> ~/.bash_profile
$ echo 'export PATH="/usr/local/opt/php@7.4/sbin:$PATH"' >> ~/.bash_profile
```

## PHPとの冒険の再出発へ
上書きまで できたら下記コマンドでPHPを再スタートさせます

```:ターミナル
$ brew services start php@7.4
```

ここで一旦ターミナルを閉じます。
大事なことなのでもう一度言います。
# ここで一旦ターミナルを閉じます。

ターミナルをもう一度起動させたらバージョンを確認してみます

```:ターミナル
＄ php -v
PHP 7.4.0 (cli)
```

これで完了です！
読書メモ「CenterNet: Keypoint Triplets for Object Detection」

#はじめに
最近、深層学習における一般物体検出技術に興味を持つようになりました。
今回の記事は、それに関連する論文の紹介となります。

CenterNet: Keypoint Triplets for Object Detection (2019)
https://github.com/Duankaiwen/CenterNet

本記事で使用したすべての図、画像は、上記の論文からのものです。

#1.論文要約
CornerNetとCenterNetを統合することで、物体検出精度を改良します。

#2.論文概要
MS-COCOデータセットを使用して、CornerNetによる算出された結果のトップ100バウンディングボックスでは、誤検出が多かったです。下記の図で、正解は青い枠線、CornerNetによる予測された物体領域は赤い枠線で表します。
![Screenshot_2019-12-05 1904 08189 pdf.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/400491/4a3d11e4-0fb6-6c03-6111-fa2a1737c13e.jpeg)

それになる理由、CornerNetというアルゴリズムでは、バウンディングボックスの内部情報を重視していないからです。下記の図では、バウンディングボックスの中心部分をチェックして、物体検出精度を改良できた例を示します。
![Screenshot_2019-12-05 1904 08189 pdf(1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/400491/f5719a3e-0996-cafb-27b3-1b85c4be87d3.jpeg)

各オブジェクトは、1つのCenter Keypointと1ペアのCornersで表現します。
以下は、CenterNetアーキテクチャとデータ処理方法となります。
![Screenshot_2019-12-05 1904 08189 pdf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/400491/0efc8050-f4e5-771f-b9aa-94ad3b2b9819.png)
> (1) select top-k center keypoints according to their scores;
(2) use the corresponding offsets to remap these center keypoints to the input image;
(3) define a central region for each bounding box and check if the central region contains center keypoints. Note that the classlabels of the checked center keypoints should be same asthat of the bounding box; 
(4) if a center keypoint is detected in the central region, we will preserve the bounding box. The score of the bounding box will be replaced by the average scores of the three points, i.e., the top-left corner, the bottom-right corner and the center keypoint. If there are no center keypoints detected in its central region, the bounding box will be removed.

論文の著者は、Cascade Corner PoolingとCenter Poolingを設計しました。
Center Poolingは水平方向と垂直方向、2つ方向の最大値を取ります、下記図の(a)で示します。Corner Poolingは境界方向の最大値だけを取ります、下記図の(b)で示します。Cascade Corner Poolingはオブジェクトの境界方向と内部方向両方の最大値を取ります、下記図の(c)で示します。
![Screenshot_2019-12-05 1904 08189 pdf(1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/400491/05293865-29da-53e9-ae01-6d7269e79642.png)

損失関数は以下のように設定されました。
![Screenshot_2019-12-05 1904 08189 pdf(4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/400491/49d40a57-827d-6953-2f4a-4574b24aa811.png)

以下は、実験の結果となります。
![Screenshot_2019-12-05 1904 08189 pdf(2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/400491/06e6573e-a4e0-b1f7-988f-5b2433c2614f.jpeg)
One-stageでは、従来の手法と比べて、より良い結果が得られました。

#3.今後
この論文のソースコードは、既に公開されましたので、
時間があったらやって見たいです。

#参考文献
1．Kaiwen Duan, Song Bai, Lingxi Xie, Honggang Qi, Qingming Huang, and Qi Tian. CenterNet: Keypoint Triplets for Object Detection. arXiv preprint arXiv:1904.08189, 2019.
【随時更新】Bitnami Redmine Win10pro プラグイン メモ

# 環境
* Windows10pro  
* Environment:  
  Redmine version                4.0.5.stable  
  Ruby version                   2.5.7-p206 (2019-10-01) [x64-mingw32]  
  Rails version                  5.2.3  
  Environment                    production  
  Database adapter               Mysql2  
  Mailer queue                    
 ActiveJob::QueueAdapters::AsyncAdapter  
 Mailer delivery                smtp

# プラグインインストール方法
## redmineインストールフォルダ/pluginsにプラグインを配置する
* 自分の環境では下記パスにプラグインを配置した  
```C:\Bitnami\redmine-4.0.5-0\apps\redmine\htdocs\plugins```

## 必要な場合、"Bitnami Redmine Stack Environment"からコマンド実行
* 下記バッチファイルを実行し、"Bitnami Redmine Stack Environment"を起動  
```C:\Bitnami\redmine-4.0.5-0\use_redmine.bat```  
各プラグインのサイト掲載されているインストール方法に沿って、コマンドを実行する  

* 【コマンド例】DBのマイグレーション

```
C:\Bitnami\redmine-4.0.5-0\apps\redmine\htdocs>bundle exec rake redmine:plugins:migrate RAILS_ENV=production
```

# 導入したプラグイン
## チケットのテンプレートを使用したい
* プラグイン： Redmine Issue Templates Plugin
* 参考サイト：[Redmineでチケットのテンプレートを設定する](https://codelab.website/redmine-issue-templates-plugin/)

## クリップボードの画像を張り付けたい
* 下記はRedmine4.xではエラーになって正常にインストールできなかった  
[Redmine clipboard_image_paste plugin](https://github.com/peclik/clipboard_image_paste)  
* [Redmine view customize plugin](https://github.com/onozaty/redmine-view-customize)をインストールし、  
[copy_image_from_clipboard](https://github.com/onozaty/redmine-view-customize-scripts/blob/master/copy_image_from_clipboard.js)を追加することで対応できた  
このプラグインは、js,html,cssのコードを追加して、色んな機能を追加できるらしい  
[サンプルのコード](https://github.com/onozaty/redmine-view-customize-scripts)

## チェックボックスを利用したい
* プラグイン：[checklists](https://www.redmineup.com/pages/plugins/checklists#pricing)
【TypeScript】フォルダごとにindex.tsを配置して一括import

下記のようなディレクトリ構成のプロジェクトがあるとする。

```
│  main.ts
│
├─repository
│      index.ts
│      PostRepository.ts
│      UserRepository.ts
│
└─service
        index.ts
        LoggerService.ts
        PostService.ts
        UserService.ts
```
各フォルダに`index.ts`を配置してそのフォルダ配下のmoduleをexportする。

たとえば`service/index.ts`は

```typescript:service/index.ts
export { LoggerService } from "./LoggerService";
export { PostService } from "./PostService";
export { UserService } from "./UserService";
```

と記述しておく。
そうするとディレクトリトップにある`main.ts`はフォルダ名のみでimportできる。

```typescript:main.ts
import { LoggerService, UserService } from './service';

const logger = new LoggerService();
const userService = new UserService();

const newUser = new User("stin_factory", 25);
userService.register(newUser);
logger.log(`${newUser.name}を新規登録しました。`);
```

記述が簡素化されて読みやすくなる。
OmniAuth【google-oauth2】のログイン時のエラーJWT::InvalidIatErrorについて

# 状況

Railsでomniauth-google-oauth2を利用してログイン認証を行っていた際に、以下エラー「JWT::InvalidIatError」が発生した。
その解決方法の備忘録になります。

<img width="629" alt="スクリーンショット 2019-12-05 16.43.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512331/312e0a89-da37-0290-ac41-5c108897418b.png">

# 解決策

## 結論

config/initializers/devise.rbに以下を追記する。
（Deviseを実装していた）

```ruby:変更前
  config.omniauth :google_oauth2, ENV['GOOGLE_APP_ID'], ENV['GOOGLE_APP_SECRET'], name: :google
```

```ruby:変更後
  config.omniauth :google_oauth2, ENV['GOOGLE_APP_ID'], ENV['GOOGLE_APP_SECRET'], name: :google, skip_jwt: true

```

## 原因

セッショントークン生成時にOSの時間のずれがあり、JWT（ジョット）のデコーディング時にエラーが発生している。
omniauth-google-oauth2の公式ドキュメントにも以下の記述がありました。

>skip_jwt: Skip JWT processing. This is for users who are >seeing JWT decoding errors with the iat field. Always try >adjusting the leeway before disabling JWT processing.


# 原因確認中に遠回りしたこと

まず、エラーの原因はOSの時間のずれであり、時間設定を変更すれば解決するという情報を発見したため、時間設定を変更しようと考えたが、それでは解決できませんでした。

Railsではシステムや環境変数で設定されている日時を
`Time.now`で取得ができます。
それとは別にapplication.rbにデフォルトのタイムゾーンを設定でき、
その日時は`Time.zone.now`で取得ができます。

現在日時を取得してみると、以下の通り

```irb:
irb(main):001:0> Time.now
=> 2019-12-05 17:16:52 +0900
irb(main):002:0> Time.zone.now
=> Thu, 05 Dec 2019 08:16:57 UTC +00:00
irb(main):003:0> 
```

`Time.zone.now`ではUTC（協定世界時：Coordinated universal time）になっているということは、RailsではデフォルトではUTC設定になっています。
こちらを日本時間（JST）に変更することにしました。

```ruby:config/application.rb
    config.time_zone = 'Tokyo'
    config.active_record.default_timezone = :local
```

上記コードを追加し、JSTにて日時取得できるようになりました。

```irb:
irb(main):001:0> Time.now
=> 2019-12-05 17:31:29 +0900
irb(main):002:0> Time.zone.now
=> Thu, 05 Dec 2019 17:31:33 JST +09:00
irb(main):003:0> 
```

しかし、エラーは解決されませんでした。今回のエラーでは関係がない模様。。。


◆参考URL
Qiita、githubのissueを参考にしました。

https://github.com/zquestz/omniauth-google-oauth2/issues/195
https://qiita.com/sutoh/items/b7d23990abb9c5083daa
https://qiita.com/takuchan9104/items/1b588e125f8a1e0c7605
QuantAnalyzerでEAの安定性を判定する

#理屈
安定したEA = 固定ロットでバックテストした際の損益曲線がほぼ直線。凸凹していない。

∴近似直線との相関係数を求めればよくね？

#やること

**C:\QuantAnalyzer4\extend\Snippets\com\strategyquant\extend\OverviewTab.SQDefault.java**
をVSCodeで編集。以下のように書き換え。Java用の拡張機能がインストールされていれば勝手にビルドされるはず。
上書き保存した後、QuantAnalyzerを起動。何かを読み込む。

```java:SQDefault.javaの179行目
replace("S4_3", "Correlation Factor", stats == null ? NA : d2(stats.getDouble(StatsConst.STABILITY)))
```

#結果（右下のCorrelation Factorに注目）
この数値は、最初のトレードと最後のトレードを結んだ直線との相関係数です。
取引回数ベースではなく、時間ベースでの計算のようです。
1に近いほど相関あり、-1に近いほど逆相関ありです。理想は0.99みたいな数字。
このヒストリーは安定して下降しているので、相関ありとなっています。
Equity Controlのところで出てくるやつだから、もしかしたら、最初の30%分のデータかもしれません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447423/d257a75b-2efe-d9ee-b4d3-6e27b7b00005.png)

#追記。こっちのほうが良くね？
前提１．実は相関係数、普通はr^2ですが、QuantAnalyzerではr^4を使っているみたいで、差がわかりやすいです。
前提２．でも４乗だと意外と差が出なくて分かりづらい（そりゃそうか）
んで、r^16としました。

```java:SQDefault.javaの179行目
replace("S4_3", "Correlation Factor", stats == null ? NA : d2(Math.pow(stats.getDouble(StatsConst.STABILITY), 4))）
```

###追記分の結果
結果の見方はあいも変わらず、１に近ければ近いほど直線に近いという話です。
いい感じに相関係数が減ってくれましたね。
直線よりも下に膨らんでいる部分が大きいグラフを描いているやつだったので、だいぶ下がっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447423/fcfcb177-df9b-c400-4777-386555e673eb.png)
[リファクタリング]リファクタのリファクタ。もしくはある日のきのこたけのこ大戦争～序～

<br>
### *[アドベントカレンダー 2019年]*
#### *6th December 2019*


\#\# これ、完全に身内用やねんな。
\#\# 外野さんは細かい突っ込みは無しで頼むんやで。
\#\# そのレベル？的な内容から書いてるでな。


**なんと今年は社内でアドベントカレンダーやりたいねーってなりました！**

でも、取り組み自体初めてだからみんなリレーで思い思いのタイトルを日替わりでやることに。
なので僕はQiitaで久々に投稿してみます。

嬉しいことに、以前作ったヤ趣~~(ヤバみな趣き)~~溢れるコードを
後輩のA氏がアドベントの初日にリファクタリングしてくれました。

なので、もう一歩深めて記事を書いていこうかなって思います。
<font color="gray">~~※※※別に悔しかったわけじゃねーし！こんなん平気だし！すごいとこ見せたいとか思ってねーし！だし！~~</font>

## 例のリファクタリングコード

前のバトンを受け継いで...
この子は席替え用に作った子ですね。
当時いた16人の研修生に対して、座席番号をユニークに割り振る機能のコードです。

```selecter.sh
#!/bin/sh
readonly NUMBER_OF_TRINEE=16
selected_seats=()
NUM=0
for i in `seq $NUMBER_OF_TRINEE`
do

  echo ENTERを押してくださいね。:
  read

  while true
  do
    NUMWK=`expr $RANDOM % $NUMBER_OF_TRINEE`

    for i in ${selected_seats[@]}
    do
      if [ $i = $NUMWK ]; then
        continue 2
      fi
    done

    NUM=$NUMWK
    break
  done

  selected_seats=("${selected_seats[@]}" $NUM)
  #結果を表示
  echo $NUM

done
echo 全員セットできました。
exit 0
```

僕がすごい雑に作ってたコードをＡ氏がリファクタリングしてくれたものが ↑ になります。
ここからのリファクタリングもまだ変数どうしようとか、課題も自身で上げてくれています。

さて、今回はこれをさらにリファクタリングする時に考えること、を考えてみましょう。

## リファクタ？　アブストラクト(抽象化)？

上からこのコードを見始めると "NUMBER_OF_TRAINEE" を起動時引数にすればいいじゃーん。
って気がしませんか？
僕はめっちゃ起動時引数にしたいです。

でも、これってリファクタリングじゃなくて抽象化(もしくは、それ以外の何か)になっちゃうんですよね。
ここでは思った通りにやってしまうと下記のような差が生まれます。

現在の機能：
「起動するとreadコマンドへのEnter毎に番号がユニークに出力される機能」

変更した後の機能：
「 **起動時引数に母数を入れて** 起動するとreadコマンドへのEnter毎に **母数までの** 番号がユニークに出力される機能」

リファクタリングって大前提が「プログラムの外部から見た動作を変えずにソースコードの内部構造を整理すること」ですので、
こういう改変はリファクタリングのフェーズでやってはいけません。

仕様が変わることって影響が大きいから注意しなきゃいけないですね。

もちろん、仕様も自分で決められる時に改良することは良いことですが、
同じ既存コードの書き換えもシーンを切り分けて考えることが重要です。

つまり、今回は "リファクタリング" を行うって決めてるので、
・「起動するとreadコマンドへのEnter毎に番号がユニークに出力される機能」
という点を変更しないで考えてみましょう。

## リファクタリングの観点

実はリファクタリングもどういう観点で実施するのか、
何のためのコードなのかによって種類があります。(僕はあると主張します。)
>
1. 内容の純粋な整理(不要な変数消したり、各種名前を整えたり。)
2. 計算回数を減らす -> 効率性がUP!
3. 見え方、ぱっと見の分かり易さを向上する -> 保守性がUP!
4. 新しい記法のトライアルで -> 結論が分かっている機能だからナレッジに転化しやすい

基本的に1.を実施後に2～4のいずれかを実施するイメージになりますね。

今回、A氏は1.の純粋な整理と言う観点ですごくきれいなコードにしてくれました。
なので、張り切って2～4を実施していきましょう。

###　でも、この企画は...
アドベントカレンダーだから続きは他の日に書くね！！！
2, 3について個別の記事になるかなぁ、と言うイメージです。

ここまで読んでくれてありがとうございましたーヽ( ･∀･)ﾉ
openssl1.1は鍵長を最後に求めるようになった

備忘録

# 問題

Macのopensslのバージョンを1.0.2kから1.1.1dに変えたら秘密鍵の生成にこけるようになった

```bash
$ openssl version
OpenSSL 1.1.1d  10 Sep 2019
$ openssl genrsa -des3 2048 -rand rand.dat
Extra arguments given.
genrsa: Use -help for summary.
```

# 今まで

別のサーバにopenssl1.0.1eだが存在したので、同じコマンドを実行したところ行ける

```bash
$ openssl version
OpenSSL 1.0.1e-fips 11 Feb 2013
$ openssl genrsa -des3 2048 -rand rand.dat
Generating RSA private key, 2048 bit long modulus
..........+++
.....+++
e is 65537 (0x10001)
Enter pass phrase:
```

# 解決

今までは鍵長は最後に指定しなくてもいけたが、1.1.1dは最後ではないとこけるらしい。
厳密にはどこのバージョンから厳密になったのかは不明。

```bash
$ openssl genrsa -des3 -rand rand.dat 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
...................................+++++
....+++++
e is 65537 (0x010001)
Enter pass phrase:
```

# 補足

マニュアルに順番書いてあるから守ろうねって話と言いたいけど
-がつかない`numbits`のみ最後の必要がある。他は順番適当でも行ける。
多分直前のoptionに対する引数みたいな扱いになるのかな(詳しくは見ていない)

> openssl genrsa [-help] [-out filename] [-passout arg] [-aes128] [-aes192] [-aes256] [-aria128] [-aria192] [-aria256] [-camellia128] [-camellia192] [-camellia256] [-des] [-des3] [-idea] [-f4] [-3] [-rand file...] [-writerand file] [-engine id] [-primes num] [numbits]
https://www.openssl.org/docs/man1.1.1/man1/genrsa.html
JavaエンジニアがGoをやりはじめて半年経ちました

[QualiArts Advent Calendar 2019](https://qiita.com/advent-calendar/2019/qualiarts)、7日目担当の朝倉です。
スマートフォンゲームのバックエンドにGoを使いはじめて半年たったのでその中で感じたことを書きます。

# はじめに
QualiArtsのスマートフォンゲームのバックエンドは、これまでJavaやNodeJSで開発していました。
今年の5月ごろから新規タイトルの開発がスタートし、新たなチャレンジとしてGoを採用して開発を進めています。
現在、新規タイトルのバックエンドエンジニアは私を含め5名で、その全員がもともとJavaエンジニアです。
長年Javaエンジニアだった私達がGoをやりはじめて感じたことを書きたいと思います。
（JavaがいいとかGoがいいとか言語の優劣を話したい訳ではありません）

# JavaからGoをやりはじめて感じたこと

## 静的型付け、コンパイル言語の安心感
スマートフォンゲームは、リリース後の運用での開発も活発で場合によっては何年も運用するので静的型付けでコンパイル時にチェックできるのはJavaと同じく安心感を感じます。
静的型付けのおかげでIDEでのコード補完も効きやすいし、他のメンバーが書いたコードも追いやすいです。

## コードの統一がしやすい
Goは標準でformatterやlinterががついているのでコードの統一がしやすいです。
標準でついてるのでコードの書き方などでチームメンバー内で対立することも少ないです。
私達のチームでは複数のlinterを組み合わせていて、不要なコードや冗長な書き方も指摘してくれるのでコードを綺麗に保つことができてるかなと思っています。

## 標準機能でだいたいのことができる
formatterやlinterの他にも、テストやテンプレートエンジンなどもGoの標準機能に含まれています。
私達のチームでは標準のテンプレートエンジンを使って各種コードや定義ファイルなどの自動生成を行っています。
また、ベンチマークやプロファイルの機能も標準でが簡単に取れるので、Javaで開発していたときよりも頻繁に計測している気がします。

## コンパイル、起動が早い
Javaと比較してコンパイルやプログラムの起動が早いです。Javaで開発していたタイトルと比べるとまだコードの量が少ないですが、コード書いてコンパイルして動作確認のストレスが少ないです。
また、Javaの時はSpringBootを使っていたのもあって起動にかなり時間がかかっていましたが、Goは起動も早いのでオートスケールとの相性も良さそうです。

## Interfaceに慣れるの時間かかる
GoのInterfaceはJavaのように明示的にimplementsする形ではなく、interfaceの中にある関数と同じシグニチャの関数が全て実装されているとそのinterfaceの型として扱うことができます。
明示的なinterfaceに慣れていたので、Goのinterfaceの扱い方に慣れるのに苦労しました。

```go
type Animal interface {
	Cry() string
}

type Dog struct {
}

func (d Dog) Cry() string {
	return "ワン"
}

func AnimalCry(animal Animal) {
	fmt.Println(animal.Cry())
}

func main() {
	// DogはAnimalインターフェースを満たしているのでAnimal型の引数に渡すことができる
	AnimalCry(Dog{})
}
```

ちなみにGoにはJavaのような継承がないですが、interfaceで満たせることが多いので今のところ継承がなくて困ることは少ないです。

## Collection操作が大変
JavaにあるGenericやStreamAPIなどはないので、GoでSlice（JavaのListのようなもの）やMapの操作は、下記のように結構ゴリゴリの実装が必要で大変です。

```go
// フィルター関数
func Filter(scores []int) []int {
	ret := make([]int,0)
	for _,v := range scores {
		if v > 50 {
			ret = append(ret, v)
		}
	}
	return ret
}
// Map変換
func ToMap(cards []*Card) map[int]*Card {
	ret := make(map[int]*Card)
	for _, card := range cards {
		ret[card.ID] = card
	}
	return ret
}
```

ゲームロジックで頻繁にSliceやMapの操作することがあるので、マスタデータやユーザデータを表すSliceの操作は自動生成しようかと考えています。

## 例外処理が大変
GoにはJavaのようなtry〜catch〜finallyやthrowのようなエラー処理はありません。
戻り値としてエラーを表すerrorインターフェースを返すことでエラー処理をします。
呼び出し元では、戻り値で返ってくるエラーを処理（さらに呼び出し元にエラーを戻すなど）する必要があります。

```go
func Func1(ctx context.Context) error {
	err := Func2(ctx)
	// error処理
	if err != nil {
		return err
	}
	・・・
}
```

Goで開発を始めた当初は面倒だなと思ってましたが、半年したらこのエラー処理にも慣れました。
エラー処理がされているかもlinterでチェックしているのでエラー処理を忘れることもなさそうです。

## 3項演算子が使いたくなる
Goには3項演算子がないので、ちょっとした分岐でも下記のようにifで分岐が必要です。

```go
func Func(isSuccess bool) int {
	value := 0
	if isSuccess {
		value = 10
	}
	return value
}
```

特に困ることはないのですが、3項演算子使いたいなぁとたまに思ったりします。


# おわりに
今回は、Javaエンジニアだった私達がGoで開発をはじめて感じたことを紹介してみました。
まだGoを使い始めたばかりで日々模索しながら開発を進めています。
これからもGoの開発で経験したことを発信できればと思います。
EC-CUBE2.xから4.xへの移行方法

これは、2019.10.24のEC-CUBE関西ユーザグループで発表した内容の補足記事です。
スライドだけでは分かりづらい部分があったため、記事として起こしてみることにしました。

<a href="https://www.slideshare.net/chihiroadachi3/20191024-eccubeug-24" target="_blank">![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72858/7d2db372-e546-f3f2-f429-4199a013e187.png)</a>

ここで紹介する移行方法は、EC-CUBE2系で運用されているサイトを、クラウド版である[ec-cube.co](https://www.ec-cube.net/product/co/)の環境へ移行する方法です。環境特有の記載もありますが、適宜読み替えていただければと思います。

# 移行全体の流れ

まず、移行作業の全体の工程としては、

- 要件の整理
- データ移行の実施
- サイト構築
- 旧サイトから新サイトへの切り替え

という流れで進めています。

# 要件の整理

2系と4系では機能差や、仕様上変更された部分があります。有償のプラグインしか提供されていないケースもありますので、事前にお客様としっかり調整しておきましょう。

機能的な変更点としては主に以下のようなものがあります。

- メルマガ機能：プラグインで代替可能
- 商品レビュー：プラグインで代替可能
- ダウンロード販売機能：有償プラグインの導入が必要(2019年12月現在)
- 定休日カレンダー：有償プラグインの導入が必要(2019年12月現在)

また、データ上での変更点としては以下のものがあります。

- 商品情報：詳細サブ情報が、フリーエリアに統一
- 会員情報：携帯メールアドレスは廃止

詳細はこちらにもまとめいています。
https://www.ec-cube.co/pdf/migration_manual_spec.pdf

# データ移行の実施

データ移行は、[データ移行プラグイン](https://www.ec-cube.net/products/detail.php?product_id=1804)を利用して行います。

使い方は簡単で、2系のバックアップ管理からバックアップファイルをダウンロードし、プラグインでアップロードするだけです。

プラグインのマニュアルも用意されているので、参考にしていただければと思います。
https://www.ec-cube.co/pdf/migration_manual_plugin.pdf

# 会員のID/パスワードの引き継ぎ

データ移行にあたってよくいただくご質問として、会員のID/パスワードはそのまま利用できるかどうか？というものがあります。

EC-CUBE同士の移行の場合、パスワードのハッシュ化を行う`AUTH_MAGIC`の値が一致していれば、移行後も会員のID/パスワードを引き継ぐことができます。

AUTH_MAGICの値は、

- 2.4の場合：data/cache/mtb_constants.php
- 2.11以降の場合：data/config/config.php

に記載されています。

4系の`.env`ファイルに、`ECCUBE_AUTH_MAGIC`という設定値に、2系の`AUTH_MAGIC`の値を記載すれば、2系のサイトでのID/パスワードでログインすることができます。

# サイト構築

データ以降が終われば、サイト構築に入ります。
デザインの調整やショップ設定、決済プラグインや、機能拡張を行うプラグインを導入していきます。

# 旧サイトから新サイトへの切り替え

旧サイトから新サイトへの切り替えは以下のような流れで実施していきます。

- 公開日の決定
- 旧サイトでの商品登録・更新の一時停止
- 新サイトの最終調整(設定の確認や購入テストなど)
- サイトの切り替えを実施

公開日が確定したら、サイト切り替えに向けた準備を進めます。

まず、旧サイト側での商品登録・更新作業は一時的に停止していただき、旧サイトと新サイトで商品情報のズレがないようにします。

並行して、新サイト側では、設定の不備がないかの確認や、購入試験を実施いただくなど、サイト公開時に問題が発生しないように確認を行います。

# 公開当日の流れ

公開当日は、以下のような流れで作業を進めます。

- 旧サイトをメンテナンス状態へ
- 会員・受注データの移行
- ドメイン切り替えの実施

まず、旧サイトでメンテナンス告知を行っていただき、移行作業中に受注や会員登録が発生しないようにします。

準備期間中に会員登録や受注が発生しているため、その間のデータを再度移行していきます。このデータ移行にあたっても、データ移行プラグインを利用します。 `会員と受注のみ移⾏する`オプションを利用することで、会員データと受注データを再度入れ直すことができます。

旧サイトと新サイトのデータの同期がとれれば、ドメインの切り替えを実施し、公開作業は完了です。

# 公開にあたっての注意点

ドメインの切り替えは、基本的にはCNAMEをec-cube.coの環境に向けるだけですが、TTLの設定値が長い場合、反映まで時間がかかってしまうため、事前にTTLの設定値を調整いただくようにしています。

また、公開当日は、お客様側での作業も多く発生するため、お互いにいつ何を行うかを明確にすることが必要になります。簡易でもよいので、タイムスケジュールを作成し、事前に調整しておくとスムーズに進行できるかと思います。

参考までに、ec-cube.coでの移行作業は以下のようなタイムスジュールで実施しています。

```
一般的な作業スケジュールは、以下のようになります。
※当日全作業を実施する場合です。時間は目安となります。

 > 旧サイトのメンテナンス・新サイトへの切り替え準備 
 • 11:00 - 11:05 旧サイトメンテナンス(御社) 
 • 11:05 - 11:10 受注・会員情報のデータ移行実施(御社) ※データ移行プラグインを利用します。
 • 11:20 - 11:30 動作確認(御社)

 > ドメイン切り替え作業
 • 12:00 - 12:15 CNAMEを設定(御社)
 • 12:15 - 12:30 SSL設定を実施(イーシーキューブ)
 ※ドメイン切り替え後、反映には数分〜数十分かかる場合があります。
```

以上です。

データ移行プラグインを使うことで、データ移行は思ったより簡単に移行できるようになっています。ぜひチャレンジしていただければよいなと思っています。
API Gateway - API キャッシュを有効にして応答性を強化する

#■概要#
API キャッシュを有効にして、エンドポイントのレスポンスがキャッシュされるようにできます。

- エンドポイントへの呼び出しの数を減らすことができる。
- API へのリクエストの低レイテンシーを実現することができる。

ステージに対してキャッシュを有効にすると、API Gateway は、秒単位で指定した有効期限 (TTL) が切れるまで、エンドポイントからのレスポンスをキャッシュします。
その後、API Gateway は、エンドポイントへのリクエストを行う代わりに、キャッシュからのエンドポイントレスポンスを調べてリクエストに応答します。

[API キャッシュを有効にして応答性を強化する](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-caching.html)



#■やってみる#

##□APIキャッシュを有効にする##
デフォルトでは無効になっているので、

- APIキャッシュを有効化にチェック
- キャッシュキャパシティ：0.5GB
- キャッシュ有効期間：180

ぐらいでやってみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/ee551ee6-1ac6-a976-ed56-f2b5771c6f33.png)

※API キャッシュを有効にすると追加料金が発生します。



##□詳細 CloudWatch メトリクスを有効化にし、動作確認する##

- 詳細 CloudWatch メトリクスを有効化にチェック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/4bbcad6b-b622-2bd4-6d0b-c3d5289186da.png)

**CacheMissCount**が1、その他は**CacheHitCount**としっかりキャッシュが効いているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/37d85b62-b816-8856-d281-55f744f03b3b.png)

|[API Gateway メトリクス](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-metrics-and-dimensions.html) | |
|---|---|
|4XXError|指定された期間に取得されたクライアント側エラーの数。|
|5XXError|指定された期間に取得されたサーバー側エラーの数。|
|CacheHitCount|指定された期間内に API キャッシュから配信されたリクエストの数。|
|CacheMissCoun|API キャッシュが有効になっている特定の期間における、バックエンドから提供されたリクエストの数。|
|Count|指定された期間内の API リクエストの合計数。|
|IntegrationLatency|API Gateway がバックエンドにリクエストを中継してから、バックエンドからレスポンスを受け取るまでの時間。|
|Latency|API Gateway がクライアントからリクエストを受け取ってから、クライアントにレスポンスを返すまでの時間。|



#■リンク#
[Amazon API Gateway](https://qiita.com/leomaro7/items/66ea3233097fa410fef9)
Environment(環境マップ)のお手軽作成＆適用方法

ここで紹介するのは、PlayCanvasの３Dハンズオン用のチュートリアル資料です。
[**"チュートリアル - PlayCanvasで作った3Dブラウザゲームにリッチな演出を追加する"**](https://support.playcanvas.jp/hc/ja/sections/360006055613-%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB-PlayCanvas%E3%81%A7%E4%BD%9C%E3%81%A3%E3%81%9F3D%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%82%B2%E3%83%BC%E3%83%A0%E3%81%AB%E3%83%AA%E3%83%83%E3%83%81%E3%81%AA%E6%BC%94%E5%87%BA%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B)からアクセスできます。
##ENVIRONMENT(環境マップ)を作成してみよう
次元コンピュータグラフィックスにおけるテクスチャマッピングの手法の1つで、3次元形状の表面に擬似的な周囲環境の映り込みを再現する手法です。この記事では環境マップの画像作成方法から、PlayCanavs上での設定までを説明していきます。
[環境マッピング - Wikipedia](https://ja.wikipedia.org/wiki/%E7%92%B0%E5%A2%83%E3%83%9E%E3%83%83%E3%83%94%E3%83%B3%E3%82%B0)
ENVIRONMENT無し
![environment2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341892/5de6d260-53b0-f58a-05d8-27a2d7904eb7.png)
ENVIRONMENT有り
![environment3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341892/1be00705-d821-27ed-32c2-7a0c6cb4133c.png)
PlayCanvasでは個々のマテリアルとワールド環境にENVIRONMENT(環境マップ)を設定できます。前述で紹介したのは個々のマテリアルに直接Cubemapを適用した例でした。
ここでは、ワールド環境への設定方法とCubeMapの作成方法を解説していきます。
##CubeMap用画像の生成
[CubeMapをお手軽の生成するジェネレーター](https://jonaszeitler.se/cubemap-toastmap-generator/)
画像をドラッグ＆ドロップすると、CubeMapを任意に位置で生成できます。
##CubeMapの生成
![environment4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341892/f6377732-3c00-2e1d-b045-8b88487b9a4a.png)
Assetsの"**＋**"ボタンまたは、右クリックから"**New Assets**"→"**CubeMap**"で作成できます。
##FACESの適用
![environment5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341892/ce512532-bad2-3866-5fb0-4dbabb5b4812.png)
生成した各画像をドラッグ＆ドロップで適用していきます。

**Prefilter**
![environment6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341892/5829b022-c78a-f979-bb8f-ea2b23b673c9.png)
Prefilterをクリックすると環境マップとして機能します。
Prefilterをオンにしないとただの画像のままなので、画像の彩度から光度を算出できません。
##RENDERINGに適用
![environment7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/341892/bac27179-2bbc-793a-72bc-3dafcaed85c2.png)
エディターの左下(歯車マーク)から、右側のインスペクター内の"RENDERING"欄に作成した"FACES"をドラッグ＆ドロップします。"Intensity"で画像の明るさ、"Mip"は1で画像のボケ無し、2～はブラーが掛かった状態になります。
Next.js + redux + TypeScript

以下など参考に整理しつつTypeScript化
https://medium.com/@bhavikbamania/a-beginner-guide-for-redux-with-next-js-4d018e1342b2

## 手順

### add package

```bash
$ yarn add redux react-redux next-redux-wrapper
$ yarn add --dev @types/react-redux @types/next-redux-wrapper
```

### store

例としてtoken用store

```ts:src/reducer/auth.ts
export const LOGIN = 'LOGIN'
export const LOGOUT = 'LOGOUT'

export interface LoginAction {
  type: typeof LOGIN
  token: string
}

export interface LogoutAction {
  type: typeof LOGOUT
}

export interface AuthState {
  token: string
}

const initialState: AuthState = {
  token: null
}

export default (state = initialState, action) => {
  switch (action.type) {
    case LOGIN:
      return { ...state, token: action.token }
    case LOGOUT:
      return { token: null }
    default:
      return state
  }
}
```

```ts:src/reducer/index.ts
import { combineReducers } from 'redux'
import auth, { AuthState } from './auth'

export type RootState = {
  auth: AuthState
}

export default combineReducers({
  auth
})
```

```ts:src/store.ts
import { createStore } from 'redux'
import rootReducer from './reducer'

export const makeStore = (initialState = {}) => {
  return createStore(rootReducer, initialState)
}
```

### Providerを_appで組み込む

```tsx:pages/_app.tsx
import React from 'react'
import App, { AppInitialProps } from 'next/app'
import Head from 'next/head'

import withRedux, { AppProps } from 'next-redux-wrapper'
import { makeStore } from '../src/store'
import { Provider } from 'react-redux'
import { LOGIN } from '../src/reducer/auth'

interface MyAppProps extends AppInitialProps, AppProps {}

export default withRedux(makeStore)(
  class MyApp extends App<MyAppProps> {
    static async getInitialProps({ Component, ctx }) {
      const token = 'TODO'
      ctx.store.dispatch({ type: LOGIN, token })

      const pageProps = Component.getInitialProps
        ? await Component.getInitialProps(ctx)
        : {}

      return { pageProps }
    }

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

      return (
        <>
          <Head>
            <title>Hello</title>
            <link rel="icon" href="/favicon.ico" />
          </Head>
          <Provider store={store}>
            <Component {...pageProps} />
          </Provider>
        </>
      )
    }
  }
)
```

### pageでuseSelectorとかする

```tsx:page/index.tsx
import React from 'react'

import { NextPage } from 'next'
import { useSelector } from 'react-redux'
import { RootState } from '../src/reducer'


const Index: NextPage = () => {
  const token = useSelector<RootState, string>(s => s.auth.token)

  return <div>{token}</div>
}

export default Index
```
[AWS]Amplify DataStore を Vue で使う

# はじめに

先日 Amplify DataStore というものがリリースされました。
詳しくは[公式ブログ](https://aws.amazon.com/jp/blogs/aws/amplify-datastore-simplify-development-of-offline-apps-with-graphql/)で確認していただければいいのですが、簡単に使ってみたところ、GraphQLの書き方をしなくてもGraphQLを使えるという感想でした。
今までAmplifyでAppSync(GraphQL)を使っていたやり方と比較しながら解説していこうかと思います。([参考記事](https://qiita.com/is_ryo/items/b9feeb017366b0cee544))

## Getting Started

まずVueのプロジェクトを作りましょう。

```
$ npm install -g @vue/cli
$ vue create amplify-datastore-sample
$ cd amplify-datastore-sample
```

amplifyの初期化などをします

```
$ npx amplify-app
```

処理が完了すると `amplify` というディレクトリが生成されています。
その中にはAmplifyで利用するAWSサービスの情報が入っていきます。
`amplify/backend/api/<datasourcename>/schema.graphql` の内容を書き換えていきます。

```graphql:schema.graphql
enum PostStatus {
  ACTIVE
  INACTIVE
}

type Post @model {
  id: ID!
  title: String!
  rating: Int!
  status: PostStatus!
}
```

AWSの環境にデプロイしていきます。

```
$ npm run amplify-modelgen
```

実行するとAppSyncとDynamoDBがデプロイされます。
これでAWS側の準備は完了です。

## Settings

必要なパッケージをインストールしておきます。

```
$ npm i @aws-amplify/core @aws-amplify/datastore
```

インストールしたパッケージをimportしておきます。
`models` というディレクトリは `npm run amplify-modelgen` を実行したときに `src` 以下に生成されます。

```ts
import { DataStore } from "@aws-amplify/datastore"
import { Post, PostStatus } from "./models"
```

## Save Data

データを書き込むときは `DataStore.save()` を使います。

```ts
await DataStore.save(
  new Post({
    title: "My First Post",
    rating: 10,
    status: PostStatus.ACTIVE
  })
)
```

このようにDynamoDBにPUTされます。
<img width="459" alt="スクリーンショット 2019-12-05 17.11.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95875/f669f2f1-a4b5-e0c7-53b9-5c46e827aef9.png">

### GraphQLで書くと…

```ts
const saveBody = `
  mutation {
    putData(
      input: {
        title: "My First Post",
        rating: 10,
        status: "active"
      }
    )
  }
`
await API.graphql(graphqlOperation(saveBody))
```

## Query Data

データを取得するときは `DataStore.query()` を使います。

```ts
const posts = await DataStore.query(Post)
```

limitや条件を指定することもできます。

```ts
await DataStore.query(
  Post,
  c => c.status("eq", PostStatus.ACTIVE),
  {
    limit: 10
  }
)
```

### GraphQLで書くと…

```ts
const queryBody = `
  query {
    queryData(limit: 10){
      items {
        id,
        title,
        rating,
        status
      }
    }
  }
`
const posts = await API.graphql(graphqlOperation(queryBody))
```

## Delete Data

データを削除するときは `DataStore.delete()` を使います。

```ts
const todelete = await DataStore.query(Post, "1234567")
await DataStore.delete(todelete)
```

ちなみに論理削除となるので、DynamoDBからデータが削除されることはありません。
`_deleted` というフラグが立ちます。
<img width="459" alt="スクリーンショット 2019-12-05 17.12.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95875/0b292df9-a1bc-ee10-0771-936385570ca7.png">

### GraphQLで書くと…

```ts
const deleteBody = `
  mutation {
    deleteData(
      input: {
        id: "1234567"
      }
    )
  }
`
await API.graphql(graphqlOperation(deleteBody))
```

## さいごに

GraphQLのquery文で結構コード量が増えたり、query文の生成がめんどくさかったりとするのですが、AmplifyDataStoreを使うと、そういったところを解消してくれるそうですね。
プロダクションレベルで使うためには認証周りだったりなどを詰めていかないといけませんが、とても使う価値がありそうです。
ではまた！
NavigationViewのアイテムを動的に隠す

## はじめに
業務において「条件に応じてNavigationViewのアイテムを隠す」
という要件があって調べたので備忘録として残しておきます。

## 実装方法
[BottomNavigationのアイテム](https://qiita.com/masaibar/items/8c6d6ab5a898233f1301 )の場合とは違い、Visibilityをいじるのが正解のようです。


```kotlin
navigationView.menu.findItem(R.id.nav_slideshow).isVisible = false
```
## 結果
Slideshowというメニューを隠してみます。

### 通常時
通常時は下記のようなアイテムを表示しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68671/af960db2-a1c5-4fef-a905-d167bbb435f8.png)

### 隠した時
このようにSlideshowが隠れました。
Visibilityをfalseにすると一見invisibleのように思えますが、挙動的にはgoneです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68671/4cadff63-abe9-5b19-f4e9-81fa0b5d9959.png)

## 参考
https://stackoverflow.com/questions/31954993/hide-a-navigation-drawer-menu-item-android