OmniAICreator/Qiita-1.07M huggingface dataset:
ブラウザBiscuitの開発で得た知見その１ - プロトタイプ/本実装/Electron

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18519/39d60c6f-1e3b-6acb-db08-aea04276e586.png)


Biscuitというブラウザを趣味で開発していまして、先日[1.0を無事リリース](https://eatbiscuit.com/ja)しました。Biscuitの開発の中で得た技術的な知見を何回かに分けて共有していきたいと思います。

## Biscuitって？

複数のアプリをまとめて管理して利用できるブラウザです。Windows、Mac、Linuxに対応しています。同種のアプリとしてはStationやFranz、Ramboxなどがあります。

特徴は、アプリをグルーピングでき視認性が高い点と、アプリごとにクッキーやローカルストレージなどが隔離され、プライベートブラウジングと同じ状態になる点です。これにより、複数アカウントを同時に運用できます。また、アカウントが入れ替わってしまって誤爆したりするミスも起こりにくくなります。

その他の特徴は[ダウンロードサイト](https://eatbiscuit.com/ja)をごらんください。

![メイン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18519/823a9318-0ff3-1833-f7cf-4876a8a97416.png)

## プロトタイプフェーズ

最初、Electron上でVue.jsを使ってプロトタイプを作成しました。Vueを選んだのは以前から使い慣れていて、手早く実装ができるという理由からです。

プロトタイプフェーズの目的は、プロダクトのMVP(Minimum Viable Product: 最低限の価値を持った製品)の検証とMVPを実現性を担保するための技術検証でした。なのでMVPに関係のない新しい技術の導入はあえて我慢しました。ブラウザなのでどうしても新しい技術も触る必要がありますが、このフェーズでは最低限に抑えることができたと思います。

プロトタイプフェーズでは以下の点について検証しました。

- 便利なブラウザを作ることは可能なのか？
    - 良いユーザー体験を実現できるか？
    - 素早く動作するか？（できればChromeと遜色ないレベルで動作してほしい）
- 技術検証
    - 複数のWebViewをタブで切り替えて動作可能か？
    - それぞれのタブでセッション情報などを隔離可能か？

実際にプロトを作ってみて、上記の点について問題がないことがわかりました。プロトにかけたのは3日間ぐらいでした。作りながら、実際に自分で使ってみて「便利かどうか？」という点もドッグフーディングしていきました。

## 本実装フェーズ

プロトで有用性、実現性が確認できたので、本格的に開発することにしました。
本実装では React/Redux/Typescrptを使いました。凝り始めるとある程度の大きさのアプリになるのが予想できましたし、プロトで技術的な基本部分はクリアできていたので、新しい技術要素を入れてもいけるんじゃないかな？と思いました。結果、この判断は大正解でした。

- React  → 複雑なビューの組み立てをコードで書ける。
- Redux → 状態管理をあまり考えなくてよくなる。全体アーキテクチャもレールに乗れる。
- Typescript → 静的型付けで開発効率が大幅に向上。安心してリファクタリングできる。

実際、この組み合わせで開発をおこなうようになってから、開発しやすく、メンテも楽な状態を保つことができるようになったと思います。(React/Reduxの部分はVue/Vuexなどでも問題ないと思いますが、今回はReact/Reduxを使いました）

## Electronについて

[Electron](https://electronjs.org/)はWebの技術でデスクトップアプリが作成できるツールです。Electronに関しては 公式ドキュメントがすごく充実しています。サンプルコードも多く、開発する上で困ることはあまりありませんでした。

ただ、ElectronはChrominumに基本的に依存しているため、開発中にElectronのバージョンアップに伴うバグが何点か発生しました。発見してものは[GitHub上のElectronのプロジェクト](https://github.com/electron/electron)にIssueとして登録しました。登録する際はElectronのIssueのポリシーに従い、

- OS
- Electronのバージョン
- 期待する動作
- 実際の動作
- 再現できるコードと再現手順
- 不具合を説明するためのスクリーンショットや動画

あたりをきちんと用意して登録しました。クリティカルなバグもいくつかあり、それらが修正されないとBiscuitがリリースできないため、丁寧ににIssueを登録しました。

中にはElectronの開発者の方の勘違いで、他のIssueとマージされて修正されないバグなどっもありましたが、適時コメントして再オープンしてもらって修正していただきました。このあたりはOSSといっても通常の開発と同じで「コミュニケーション重要」だと思います。

## 使用したElectron関連のライブラリ

Electron本体と関連する以下のライブラリを使いました。

```json
  "dependencies": {
    ...
    "about-window": "^1.13.0", // Aboutダイアログ
    "electron-context-menu": "^0.12.1", // コンテキストメニュー作成
    "electron-dl": "^1.14.0", // ファイルダウンロードのヘルパ
    "electron-find": "^1.0.5", // ブラウザ内でのキーワード検索
    "electron-is-dev": "^1.1.0", // 開発モード判定
    "electron-log": "^3.0.5", // ログ
    "electron-squirrel-startup": "^1.0.0", // Windowsでアプリのデスクトップショートカットを作成
    "electron-updater": "^4.0.6", // 自動更新
    "electron-window-state": "^5.0.3", // Windowのサイズなどを保存
    ...
  },
  "devDependencies": {
    ...
    "electron": "^6.0.0-beta.10", // Elecyron本体
    "electron-builder": "^20.44.4", // パッケージング
    "electron-notarize": "^0.1.1",  // Macのアプリ認可自動化
    "electron-reloader": "^0.3.0",  // 開発時のオートリロード
    ...
}
```

それぞれのライブラリについては次回書きます。
Asset Catalogで同じ名前の別画像を利用する

# はじめに
- Xcodeのアセットカタログ（デフォルトではAssets.xcassets）は、画像などのリソースを管理します
- アセットカタログ内で、フォルダ分けも可能です
- その際、別のフォルダに同じ名前の画像を置きたくなることもあります
- その場合の扱い方です

# 検証環境
- Xcode 10.2.1
- iOS 12.2
- Swift 5

# フォルダにNamespaceを付与する
## デフォルトの状態（Namespaceなし）
<img width="960" alt="no_namespace.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100115/6a948b85-5d67-d33b-ad9c-dc2033911760.png">

- この画像の例では、`bird`という画像が`forest`フォルダと`sea`フォルダの両方に配置されています
- ですが、デフォルトの状態ではフォルダ名は無視されるので、これらの画像には`bird`という名前でアクセスすることになり、区別ができません

## フォルダ名付きでのアクセス（Namespaceあり）
<img width="960" alt="with_namespace.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100115/a7018e22-5500-013a-ee08-074e686ea7a4.png">

- アセットカタログ内でフォルダを選択し、Attributes Inspectorから`Provides Namespace`にチェックを入れると、そのフォルダ名がNamespaceとして利用されます
    - フォルダの色も黄色から水色に変わっていますね
- この画像の例では、それぞれ`forest/bird`と`sea/bird`という名前で区別されます
- こんな感じで、コード内でもInterface Builderでも参照できます

    ```swift
    imageView.image = index == 0 ? UIImage(named: "forest/bird") : UIImage(named: "sea/bird")
    ```

    <img width="643" alt="image_ib.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100115/9e2e4286-4055-8692-4677-a19aaae9d86e.png">

# まとめ
- Namespaceがあると、格段に管理がしやすくなりますね
- 今回作成したサンプルコードは、GitHubに置きました
    - [aokiplayer/AssetFolderSample](https://github.com/aokiplayer/AssetFolderSample)
- なお、この記事は[私のブログ記事](https://yagamo-style.com/posts/assetfolder/)からの転載です

# 参考
- [Asset Catalog Format Reference: Folders](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_ref-Asset_Catalog_Format/FolderStructure.html#//apple_ref/doc/uid/TP40015170-CH33-SW1)
- [ios - Asset Catalog: Access images with same name in different folders - Stack Overflow](https://stackoverflow.com/questions/33284412/asset-catalog-access-images-with-same-name-in-different-folders)
「社内ニートになろう」.batでブランチを切る。

######※これはgit等で管理する必要は特にないが、バックアップとしてブランチを切りたい人向けの記事です。

```.bat
rem コピー元フォルダ名
SET CopyFromFolder=C:\[Path]
rem コピー元フォルダ名
SET CopyToFolder=C:\[Path]
rem zip化する為のファイル名
SET ZIPname=C:\[Path]\backup\%date:~0,4%%date:~5,2%%date:~8,2%%time:~0,2%%time:~3,2%%time:~6,2%.zip
rem zip化したデータを移動させるためのファイル名(どこかにzipを移動させたい場合のみ有効)
SET ZIPMove=C:\[Path]\backup

xcopy /Y /I /H /S /E %CopyFromFolder% %CopyToFolder%

zip -r -9 %ZIPname% %CopyFromFolder%

move /Y %ZIPname% %ZIPMove%
```

正確にはこれでブランチを切っていると言われるといささか不明な点も出ますが、バックアップをした後、日付を付けてzip化しているので、ある程度は不具合を起こしてしまったとしても戻ることが出来ます。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289160/7a163036-90b6-08b1-30fd-b11f18e6d94a.png)

結果として上記のような大量のzipが出来る事になるが、PCの電源を入れたタイミングや特定の時間で動かすよう、タスクスケジューラーやスタートに登録しておくといいと思います。

初投稿の為こんな小さい記事になりましたが、間違い等があればご指摘頂けると幸いです。
Flutter製iOSアプリのFABで、スペース区切りのlabelが改行される

# 事象
FABのlabelに渡したTextが、スペースで改行される。
（ちなみにスペースを2つ入れると改行されません。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136368/d15d46aa-2f77-a91a-150f-93c0e41d0323.png)

```dart:
FloatingActionButton.extended(
  icon: Icon(Icons.gamepad),
  label: const Text("START GAME"),
  shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(16)),
);
```

# 暫定対応
Textに`TextStyle(letterSpacing: 1)`を設定することで、事象が改善されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136368/6ae64fdf-b171-d0aa-a681-e869335329b3.png)

```dart:
FloatingActionButton.extended(
  icon: Icon(Icons.gamepad),
  label: const Text("START GAME", style: TextStyle(letterSpacing: 1)),
  shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(16)),
);
```

# 根本原因
内部実装など追えていないため、何故改行されるのか、何故letterSpacingを入れると解消されるのかはまだ判明していません。
分かり次第追記します。
React.js 学習メモ　第8回

最近Reactの勉強を始めたので、ノート代わりにまとめていきます。
主に自分の学習の流れを振り返りで残す形なので色々、省いてます。
Webエンジニアの諸先輩方からアドバイスやご指摘を頂けたらありがたいです！

#importしている理由 
import は別ファイルに切り出した JavaScript (module) から変数、関数、クラス等を読み込むための宣言!
変数、関数、クラス等を、別ファイルに切り出し、それを読みこむために import を使う。
そのようなモジュールを読み込むためにimportを利用します。

>PHPであればrequireという構文があります。

外部で利用したいクラスに『export』を付けることで『import』が可能となります。

>JavaScript におけるモジュールとは、別ファイルに切り分けられた変数、関数、クラス等のこと。これを用いるために import / export を使う。


読み込む側では、import {名前} from 'ファイル名' とする。

```jsx
//importで外部クラスを読み込む際のコード
import {'module neme'} from 'path to module'
```

#exportしている理由
外部ファイルのクラスの読み込み
アプリケーションが大きくなるとモジュール毎に機能を管理していく必要があります。
出力する側では export をしておく。

#exportの方法は２種類
###export class Member {} ・・・名前付きexport

出力する側で変数、関数、クラスの前に export をつける。
この場合のファイル名は、Hello のように、拡張子をつけなくていい。

```jsx

export class Hello extends React.Component {
  render() {
    return <div>Hello</div>;
  }
}
```

###export defualt class {}・・・デフォルトのexport


export default したものに関しては、import するときに {} を使わない。
1 ファイル内で、1 export default だけしかできない。(通常の export は複数できる)

```jsx
//名前付きexportの場合
import { Hello } from '../'
//デフォルトのexportの場合
import Hello from '../'
```
そのため、1ファイルに1コンポーネントだけを作る場合には、default を使う場合がスタンダード

export default する際の書き方

```jsx
// class の定義をすると同時に 
// export default する書き方
export default class Hello extends React.Component {
  render() {
    return <div>Hello</div>;
  }
}
```

```jsx
// 一旦 class の定義をしてから
class Hello extends React.Component {
  render() {
    return <div>Hello</div>;
  }
}

// 定義した class を export default するやり方
export default Hello;
```

#振り返り
今回は、import,exportについてまとめました。export defaultと名前付きのexportがどのような物であるのかを明確にできたのは初めよく分からない点だったので良かったです。
WebSocket の検証では wscat が便利だった

[WebSocket の動作確認に wscat が便利すぎる件](https://tricknotes.hateblo.jp/entry/20120227/p1)

[WebSocketに対応したCloudFrontをwscatで疎通確認してみた](https://dev.classmethod.jp/cloud/aws/cloudfront-websocket-test-by-wscat/)


既に記事がありましたが、自分メモ。

# どんな時に使う？

WebSocket の接続試験を行う時に、いちいちサーバー・クライアント側でプログラムを書くの面倒だなーと思っていたら、調べてみた所 `wscat` という便利な npm があったので使ってみました。

+ [wscat-npm](https://www.npmjs.com/package/wscat)
+ [websockets/wscat](https://github.com/websockets/wscat)


# インストール方法

Node.js が入っていれば `npm` コマンドでインストール可能。

```bash
$npm install -g wscat
```

Node.js がまだ入っていない場合には各 OS などに併せてインストールしておいて下さい

# サーバー

`-l` オプションを指定してコマンドを実行することで指定したポートで WebSocket を受け付ける(listen)事ができます。
以下は 8000番ポートで受付を行う際の例

```bash
$wscat -l 8000
```

# クライアント

サーバー側が HTTP の場合、`ws`スキームを使い、HTTPS の場合には `wss`スキームを利用する必要があります。

[Difference between ws and wss?](https://stackoverflow.com/questions/46557485/difference-between-ws-and-wss)


```bash
# HTTP
$wscat -c ws://example.com:8000

# HTTPS
$wscat -c wss://example.com:443

```
FlutterのAdMobプラグインを入れたiOSアプリが起動時にクラッシュする

# 事象
アプリ起動時に画面が真っ暗になり、アプリがクラッシュする。

# クラッシュログ

```
*** First throw call stack:
(
	0   CoreFoundation                      0x000000010c2956fb __exceptionPreprocess + 331
	1   libobjc.A.dylib                     0x000000010b839ac5 objc_exception_throw + 48
	2   CoreFoundation                      0x000000010c295269 -[NSException raise] + 9
	3   Runner                              0x000000010248bd91 GADVerifyApplicationID + 148
	4   Runner                              0x000000010248c9c0 GADVerifyApplicationID + 3267
	5   libdispatch.dylib           <…>
Lost connection to device.
```

# 原因
`GADApplicationIdentifier（AdMobアプリID）`を`Info.plist`に追記していなかった。

AndroidでAdMobプラグインを導入する際には、`AndroidManifest.xml`にAdMobアプリIDを記載する必要がありますが、iOSでも同様の処理が必要だったようです。
こちらの公式ドキュメントに記載がありました。
https://firebase.google.com/docs/admob/ios/quick-start?hl=en#update_your_infoplist

# 解決策
`Info.plist`に下記の`<key>, <string>`を追記します。

```plist:Info.plist
<dict>
  <key>GADApplicationIdentifier</key>
  <string>[MY-ADMOB-APP-ID]</string>
</dict>
```
CircleCIでPuppeteerを定期実行する

## 概要

- PuppeteerをCircleCIで毎日決まった時間に実行するというものを作ったのでそのメモ

## 雛形作成

- サンプル用の雛形を作る

```bash
mkdir puppeteer-sample
cd puppeteer-sample
yarn init -y
# or
npm init -y
```

## Puppeteerの導入

### インストール

```bash
yarn add puppeteer
# or
npm i puppeteer
```

### サンプルの作成

- 今回はPuppeteerのリポジトリにあるサンプルを使う
    - https://github.com/GoogleChrome/puppeteer/blob/master/examples/search.js
    - [https://developers.google.com/web/](https://developers.google.com/web/)の中から`Headless Chrome`のタグがついた記事を抽出するというサンプル


```js:sample.js
const puppeteer = require('puppeteer');

(async() => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.goto('https://developers.google.com/web/');

  // Type into search box.
  await page.type('#searchbox input', 'Headless Chrome');

  // Wait for suggest overlay to appear and click "show all results".
  const allResultsSelector = '.devsite-suggest-all-results';
  await page.waitForSelector(allResultsSelector);
  await page.click(allResultsSelector);

  // Wait for the results page to load and display the results.
  const resultsSelector = '.gsc-results .gsc-thumbnail-inside a.gs-title';
  await page.waitForSelector(resultsSelector);

  // Extract the results from the page.
  const links = await page.evaluate(resultsSelector => {
    const anchors = Array.from(document.querySelectorAll(resultsSelector));
    return anchors.map(anchor => {
      const title = anchor.textContent.split('|')[0].trim();
      return `${title} - ${anchor.href}`;
    });
  }, resultsSelector);
  console.log(links.join('\n'));

  await browser.close();
})();
```

## CircleCIの設定

- 設定ファイルを作成する
- 今回は6時間ごとに実行されるサンプル

```yaml:.circleci/config.yml
version: 2
jobs:
  execute:
    docker:
      - image: circleci/node:latest-browsers
    steps:
      - checkout
      - run: yarn install
      - run: node sample.js
workflows:
  version: 2
  execute-workflow:
    triggers:
      - schedule:
          cron: '0 0,6,12,18 * * *' # UTC
          filters:
            branches:
              only:
                - master
    jobs:
      - execute
```

- dockerイメージに`circleci/node:latest-browsers`を指定するとこれだけでPuppeteerが動いた
- scheduleにcronの設定で実行タイミングを指定できる
    - `0 */6 * * *`のような書き方だとエラーになってしまって少しはまった

## 実行

- ソースコードをGithubにpushしてCircleCIと連携させる

<img width="577" alt="スクリーンショット 2019-06-25 8.01.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/1789a396-953e-d4f0-b2c1-a62ad729d073.png">

- タイミングが来ると自動で起動される

<img width="1023" alt="スクリーンショット 2019-06-25 8.03.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/c151cfb6-1015-18cd-2657-8af66d8ed612.png">
ComparableとComparator(オブジェクトの順序付け)

#とても紛らわしい
 ソートしたければHashSetではなくTreeSetを用いればよいが、自分のオリジナルの順序付けを施したいことがある。そんなときにComparatorやComparableが有効である。

#Comparable
  java.lang.Comparable<T> インターフェース「比較可能」
(1)TreeSetクラスは、内部で要素を並べ替える時に__Comparable<T>型__にキャストする処理を行なっている。
(2)標準ライブラリのStringクラスやIntegerクラスなどの値クラスはComparableインターフェースを実装している。


```java:Myclass.java
class MyClass implements Comparable<Myclass>{
  //抽象メソッドの実装
  public int compareTo(Myclass obj){
    return this.id - obj.id; 
  }
}
//昇順
```

#Comparator
 java.util.Comparatorインターフェース 「比較器」
Comparatorが利用されるモチベーションは、TreeSetやTreeMapなどに格納される要素の並べ替え順序をデフォルトとは別の順序に変更したいような時である。

```java:Myclass.java
class Myclass implements Comparator<String>{
  @Override 
  public int compare(String obj1, String obj2){
     return obj1.length() - obj2.length();
  }
}
//昇順
```

|コンストラクタ|            説明             |
|:----------------------------------:|:---------------------------:|
|TreeSet(Comparator＜? super E＞ comparator)|指定されたコンパレータにしたがってソートされた新しい空のツリーセットを作成する|

TreeSetにはComparatorオブジェクトを引数にとるコンストラクタが宣言されている(OraDocsに記載されている)ので任意の並べ替え順序を適用できる。


#まとめ
Comparableは「自分と他のオブジェクトを比較」　　　
Comparatorは「他の2つのオブジェクトを比較」
(備忘録:ComparableやComparatorが紛らわしいというよりかは、TreeSetやTreeMapの定義があやふやなのかも)
GitのデフォルトエディタをVisual Studio Codeに設定

#目的
gitコマンドで使われるエディタをVisual Studio Codeに設定。
#方法
- Visual Studio Codeで「command + shift + p」でコマンドパレットを開き下記を実行する。

※windowsの場合は「Ctrl+Shift+P 」でコマンドパレットを開く。

```
Shell Command: Install 'Code' command in path
```
<img width="850" alt="Screen Shot 2019-06-25 at 7.31.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337592/1baa9b12-604c-c39d-c6b0-6972dfb84490.png">

- コマンドラインから下記のコマンドを実行する。

```
git config --global core.editor "code --wait"
```
- これでgit commitした際にVisual Studio Codeが起動すればOKです。

#補足
最初にコマンドパレットでインストールせずに、gitのeditorにcodeを設定してcommitすると以下の様なエラーが出てました。
同じエラーが出てた方はぜひ試してみてください。

```
error: There was a problem with the editor 'code'.
Please supply the message using either -m or -F option.
```
(GCE + StackdriverMonitoring) 機械学習の処理を含む定期バッチの監視のtips

機械学習を活用した計算処理の定期バッチを監視し始めて1ヶ月経ったので、アウトプットしてみる。

:point_up:前提:point_up:

- 機械学習・GCP(GCE + Stackdriver Monitoring)・Slackに関わる話がメイン
- Stackdriver Monitoringの細かな設定方法は省略


# Tips1:タスク別に監視する
`1GCEインスタンス:1タスク`が前提。
GCEインスタンスにタスク別にタグをつけることによりタスク別に監視設定を作ることができる。

### なぜタスク別に監視するのか
- GCEを使っていなくても、内部的にGCEを使っているサービス?のモニタリングデータが混ざってくる
    - GAE/FEを同GCPプロジェクト内で使用している際にモニタリングデータが混ざってきた
- タスク毎に理想の処理時間やリソースを使い切りの程度が異なるので、タスク毎に異なるAlertPolicyを作成するべきだから

### 手順
- タスク別に監視できていない状態
    - <img width="1040" alt="0.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/87a6ed91-39f5-4cc8-b9c7-31836c45adf5.png">
- インスタンスにタグをつける(タスク毎に分類したタグを使用)
    - <img width="647" alt="1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/9f25842e-011b-1151-09db-92599442f487.png">
- インスタンスにタグが着いたことを確認
    - <img width="603" alt="2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/f3e5d752-8946-9338-4f64-bdc97e2c1a6f.png">

- タスク毎にStackdriverMonitoringグループを作成する
    - この際、タグが着いたインスタンスが存在していないと、グループを作成する際の条件にTagが表示されない
    - <img width="871" alt="3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/d3b1be3a-07e5-1d68-dd57-9f46c3184a34.png">

- DashBoardやAlertを作成する際の条件でStackdriverMonitoringグループを指定する
    - <img width="1045" alt="4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/e50bc688-98a0-72d8-c2fb-0c14d43e961d.png">
- タスク別にモニタリングデータを絞りこ込めたことを確認
    - <img width="1043" alt="5.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/f92d6bd1-62b8-a75b-24c1-8a741b6cb742.png">


# Tips2:StackdriverMonitoringのSlackアプリを使用してお知らせする

### なぜアプリを使うのか(webhookではダメなのか)
- Slackに通知した際にincident発生なのか、incident stoppedなのか分かりやすい
    - <img width="567" alt="6.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/3555b386-1d2c-b56e-5848-89ce76c847dc.png">

### 手順
- https://www.topgate.co.jp/gcp19-stackdriver-monitoring-notify-mail-slack
    - topgateさんの説明が分かりやすいので見てくださいw



# Tips3:処理時間の監視設定
タスク毎にGCEインスタンスをcreateしてshutdownするのが前提だが、Uptime(稼働時間)を処理時間と読み替えて設定する。

### 手順
- Metricの`Uptime`を選択する
     - threshold(閾値)を1に設定し、任意の時間とConditionを設定する
         - 単位がms/sなのでthresholdを1にして運用している
     - <img width="1235" alt="7.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/392a9ea4-3c7a-2e68-87cb-f8492db04ae9.png">


# Tips4:定期実行タスクの死活監視設定
機械学習のタスクが失敗したらマシンリソースの負荷が上がって来ないという考え方で、メモリ使用量の監視を設定する。
(起動シェルの監視だけでも良いかもですが....)

### 手順
- MetricにMemory utilizationを選択し、Filterにstate="used"を選択し、Conditionにis belowを選択し、任意の閾値と時間を設定する
    - <img width="1230" alt="8.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/78e0018d-2d7b-d013-0ea9-368192f9eb33.png">

# Tips5:サーバーリソース不足の監視設定
機械学習やっていると限界までサーバーリソースを使いこむので、リソースの監視を設定する。

### 手順
- MetricにMemory utilizationを選択し、Filterにstate="used"を選択し、Conditionにis aboveを選択し、Forにmost recent valueを選択する(メモリが閾値を超えた瞬間にalert)
    - <img width="1231" alt="9.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/2d59ac1b-1733-7f20-7a5f-29e1e852151a.png">



以上、新しい発見があったら追加して行く。
TypedefはFunctionを使って定義しよう。

typedefで関数型を定義出来ますが、Functionを使いましょうというお話です。

Stringを引数にとり、int型を返す関数は

```dart
typedef int LegacyTypeDef(String message);

int echo(String message){
  print(message);
  
  return 0;
}
void echoWithoutReturn(String message){
  print(message);
}

LegacyTypeDef legacyTypeDef = echo;
legacyTypeDef('legacyTypeDef');

//LegacyTypeDef legacyTypeDef = echoWithoutReturn; //戻り値型が違うのでエラー
```
で定義&使用できますが、

```dart
typedef int LegacyTypeDef(String);

LegacyTypeDef legacyTypeDef = echo; //エラー
```
だとエラーになります。引数の所に型(String)しか書いていない場合は引数名と解釈され、dynamic型のStringという引数を取る関数と見なされてしまうからです。使わない引数名も定義に含めないといけないのは嫌ですよね。そこでFunctionを使います。

```dart
typedef NewTypeDef = int Function(String); //Functionを使えば引数名なしでOK

NewTypeDef newTypeDef = echo;
newTypeDef('newTypeDef');
```
Genericも使えます

```dart
//Generic使う
typedef NewTypeDefWithGeneric<T,M> = M Function(T);

NewTypeDefWithGeneric<String,int> newTypeDefWithGeneric = echo;
newTypeDefWithGeneric('newTypeDefWithGeneric');  
```
ここまでをまとめるとこんな感じ。

```dart
//typedef int LegacyTypeDef2(String);  //引数名省略するとdynamic型の引数名Stringと解釈されてしまう
typedef int LegacyTypeDef(String message);
typedef NewTypeDef = int Function(String); //Functionを使えば引数名なしでOK

//Generic使う
typedef NewTypeDefWithGeneric<T,M> = M Function(T);

void main() {
  LegacyTypeDef legacyTypeDef = echo;
  //LegacyTypeDef legacyTypeDef = echoWithoutReturn; //戻り値型が違うのでエラー
  NewTypeDef newTypeDef = echo;
  NewTypeDefWithGeneric<String,int> newTypeDefWithGeneric = echo;
  
  legacyTypeDef('legacyTypeDef');
  newTypeDef('newTypeDef');
  newTypeDefWithGeneric('newTypeDefWithGeneric');  
}

int echo(String message){
  print(message);
  
  return 0;
}

void echoWithoutReturn(String message){
  print(message);
}
```

Dart公式もFunctionを使う方法を推奨しています。
Effective Dart:https://dart.dev/guides/language/effective-dart/design#dont-use-the-legacy-typedef-syntax

Lint:https://dart-lang.github.io/linter/lints/prefer_generic_function_type_aliases.html
ローカルでrails sが止まらない！

## ユースケース
railsのゾンビプロセスを発生させる手法

- tmux, terminalを閉じてしまった場合
- railsをバックグラウンドで実行する方法

railsのバックグラウンド実行

```shell
$ rails server -d
```

- process idを消す

```shell
$ rm ./tmp/pids/server.pid
```

## サーバーを止める方法

process id(以下pid)を消す必要があります。
消し方としては以下の3つの方法があります。

- ポートからpidを探す
- tmpのserver.pidからpidを探す
- pumaのprocessからpidを探す

### ポートからpidを探す

```shell
$ lsof -i:3000
COMMAND   PID  USER   FD   TYPE            DEVICE SIZE/OFF NODE NAME
ruby    31584 _user_name_   10u  IPv4 0xac1c1f275d70c41      0t0  TCP localhost:hbci (LISTEN)
ruby    31584 _user_name_   11u  IPv6 0xac1c1f26fce7e19      0t0  TCP localhost:hbci (LISTEN)
$ kill -kill 31584
```

### tmpのserver.pidからpidを探す
```shell
$ ls tmp/pids/server.pid
tmp/pids/server.pid
$ cat tmp/pids/server.pid
31584
$ kill -kill 31584
```

### pumaのprocessからpidを探す
```shell
$ ps aux | grep puma
_user_name_            32578   0.0  0.0 xxx xxx s000  S+   xx:xxAM   0:00.00 grep puma
_user_name_            31584   0.0  0.8 xxx xxx s005  S+   xx:xxAM   0:02.52 puma 3.12.1 (tcp://localhost:3000) [project_name]
$ kill -kill 31584
```

## 参考文献
- [ローカルサーバを停止せずにターミナルを閉じてしまいました。](https://qiita.com/maru_katy/items/8348a607b5f394c8d935)
- [Ruby on Railsをバックグラウンドで実行する。止める。](https://koooza.net/post-323)
【Python】TIPS:pandas.read_csv時のUnnamed回避【pandas】

# 概要
- pandasのread_csv時に、カラムが空欄の時にUnnamedで読み込んでしまう現象の回避方法
- pandasは0.24以上の機能を使用

# 問題
カラムに複数の空欄がある場合、Unnamedが勝手に入力されてしまう。
データフレームは、4行のカラムを含んでいる。

```python:問題

concat_df = pd.read_csv(r'**.csv',
                        header=[0,1,2,3],
                        keep_default_na=False,
                        index_col=[0],
                        parse_dates=[0],
                        encoding='shift-jis',
                        engine='python'
                       )

```
![qiita1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148213/77524de3-c2d1-4ad1-2c37-d540e47263d6.jpeg)


# 解決方法
read_csvの際にヘッダを読み込まず、あとでMultiIndexとしてカラム名を登録する

```python:解決方法
raw_df = pd.read_csv(r'**.csv',
                     keep_default_na=False, # 空欄をNaで置き換えない
                     header=None, # headerは指定しない
                     index_col=[0],
                     encoding='shift-jis',
                     engine='python'
                     )

# MultiIndex.from_frameを使用する。
# raw_dfの4行目までをカラムとしてMultiIndexを作り、raw_dfのカラムとして入力する
raw_df.columns = pd.MultiIndex.from_frame(raw_df.iloc[:4, :].T)

# カラム名とデータフレームの4行目までは重複するので削除する。
raw_df = raw_df.iloc[4:, :]
```
![qiita2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148213/d3c87a7e-8734-6a27-70da-c4da9f7b1dac.jpeg)
[Swift] 初心者向け 失敗しないScrollViewの設定のしかた 

##はじめに
初めてScrollviewの設定をする際に、少々手こずったので自分のためにまとめておきました。
自分なりに一番納得した手順を説明します。何か指摘や質問があれば気軽にコメントください。
##手順

1.ScrollViewの追加
　元からあるViewにScrollViewを追加します。
<img width="400" alt="ScreemShot.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377767/f836a652-8351-ea83-d245-4ef301fa5553.png">

2.AutoLayoutの設定
　ScrollViewに対し、top,bootom,left,rightそれぞれを以下のように０に設定する。
<img width="267" alt="ScreenShot2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377767/7e15200b-5bd8-b133-0c89-b01226406e44.png">
以下のように対象がSafeAreaになっていることを確認します。
<img width="368" alt="ScreenShot3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377767/b2962113-5640-082c-f8ba-ef1f25857fea.png">

3.ScrollViewの上にViewを追加
  1と同じようにScrollViewの上にViewを追加します。

4.追加したViewに対しAutoLayoutを設定
　まず、2と同じように追加したviewのtop,bottom,left,rightを0に設定します。
　次に、commandキーを押しながら追加したviewとScrollViewを選択し、以下のようにequal Widthに設定します。
　**（注意）equal Heightには設定しないようにしてください。**
<img width="265" alt="スクリーンショット 2019-06-25 5.46.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377767/829259c8-c97b-2b3e-a2d3-661d3426e693.png">

5.スクロールできるviewの高さを決める。
  追加したviewの高さを好きな値に設定して完成。
  (今回は1000にしました。)
<img width="265" alt="スクリーンショット 2019-06-25 6.02.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377767/c750040e-acc6-669a-b4fc-b2282256a430.png">


##StoryBoardの設定
 storyboardでUIの作成を行う時には以下の設定をして、StoryBoardに表示されるviewの大きさを変更します。
1. main.storyboardでviewControllerを選択
2. 右のウィンドウからsimulatedSizeをFreedomに設定する。
　 以下のようにsimulatedSizeをFreedomに設定し、Heightの値をview全体が表示される値に設定します。
<img width="258" alt="スクリーンショット 2019-06-25 6.13.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377767/d3564438-a91d-ed1d-22b1-586910abc714.png">

3.以上

##さいごに
ScrollViewの設定についてまとめました。
次は、僕がお気に入りのライブラリについて紹介していきます。
気軽にコメントや質問ください。
なぜか netlify にデプロイできなぁーい

なぜか netlify にデプロイできないケースがある。 
```failed during stage 'building site': Build script returned non-zero exit code: 1```

nuxt generate でのデプロイでしたが、今回は import している component ファイル名が大文字小文字が違っていたので、メモ。

```
from HogeHoge import(`~components/HogeHoge`)
```

ファイル名
○ /components/HogeHoge
✕ /components/hogeHoge

ローカルの yarn generate だと通ってしまうのやめてくれ、、
UnboundでCNAMEレコードを返却する

Unboundを使って、任意のドメインに対してCNAMEで別のドメインを返却し、そのドメインの名前解決を外部DNSへ再帰問い合わせする、という機能が実現できるか調査した。

#### 結論

仕様として意図された振る舞いかどうかは不明だが、local-zoneのtype（local-dataに合致しない問い合わせへの応答方法）をredirectに設定すると、CNAMEレコードに設定した外部ドメインを再帰問い合わせして返却できる。（typeをtransparentに設定すると再帰問い合わせが行われない。）

````
local-zone: "cname.dummy" redirect
local-data: "cname.dummy. CNAME google.com."
````

unboundのマニュアルからはauth-zoneとstub-zone（もしくはforward-zone）を設定するのが本来の使い方のようであるが、下記の設定では再帰問い合わせが実現できなかった。

```
auth-zone:
  name: cname.dummy
  zonefile: ./zone.conf
forward-zone:
  name: google.com
  forward-addr: 8.8.8.8
```

zone.conf

```
cname.dummy. IN CNAME google.com.
```

#### 検証方法・結果

##### docker-compose.yml

```yaml
version: '3'
services:
  unbound:
    image: mvance/unbound
    hostname: unbound
    ports:
      - 53:53/udp
      - 53:53/tcp
    volumes:
      - ./local-zone.conf:/opt/unbound/etc/unbound/a-records.conf
    restart: always
  shell:
    tty: true
    build:
      context: .
    depends_on:
      - unbound
    command: dig @unbound cname.dummy
```

##### local-zone.conf

```
local-zone: "cname.dummy" redirect
local-data: "cname.dummy. CNAME google.com."
```

##### 実行結果

```
# docker-compose up
...
shell_1    | ;; QUESTION SECTION:
shell_1    | ;cname.dummy.                      IN      A
shell_1    |
shell_1    | ;; ANSWER SECTION:
shell_1    | cname.dummy.               3600    IN      CNAME   google.com.
shell_1    | google.com.                300     IN      A       172.217.25.206
...
```

#### 参考

* [unbound.confマニュアル](https://nlnetlabs.nl/documentation/unbound/unbound.conf/)
* メーリングリスト
   * [[Unbound-users] Can't get CNAME entries to resolve](https://nlnetlabs.nl/pipermail/unbound-users/2009-March/000509.html)
     * Unboundは全ての機能をサポートした権威サーバーとして設計されていないため、CNAMEのように設定値の解釈（再帰問い合わせ）が必要なレコードはstub-zoneを使って、他の権威サーバーに転送する必要があるとのこと。
   * [auth-zone and CNAME record...still not working?](https://nlnetlabs.nl/pipermail/unbound-users/2018-March/005119.html)
     * auth-zoneとstub-zoneの設定でCNAMEレコードの再帰問い合わせが発生しないという投稿に対して、redirectを使う方法（今回の解決策）の提案と、`for-downstream: no`をすればよい（この方法では期待通り動作しなかった）との助言がされている。
   * [[Unbound-users] Local-data CNAME-Records seems not to work properly](https://nlnetlabs.nl/pipermail/unbound-users/2015-March/003805.html)
     * CNAMEレコードの設定について、stub-zoneを設定する必要があるとの回答がされている。
真理表とブール代数

未来電子テクノロジーでインターンをしている田島一毅です。プログラミング初心者であるため、内容に誤りがあるかもしれません。
もし、誤りがあれば修正するのでどんどん指摘してください。

今回は、2値論理とブール代数について、面白いと思ったのでシェアします。

#2値論理とは
2値論理とは、ある命題について真(TRUE)または偽(FALSE)で真偽を示す論理のことです。

例えば、「夏目漱石は猫である。」という命題は、夏目漱石が人間であるため、偽(FALSE)です。

しかし、命題の中には、真(TRUE)と偽(FALSE)で真偽を示せない命題があります。

例えば、「吾輩は猫である。」という命題は、「吾輩」が「猫」である場合もあれば、「人間」である場合、いやもしかしたら「ゾウ」である場合も考えられます。

要するに不明(UNKNOWN)ってわけです。

このように、真(TRUE)と偽(FALSE)と不明(UNKNOWN)の3パターンで示す論理を3値論理といいますが、今回は真(TRUE)と偽(FALSE)の2つで示せる2値論理を考えることにします。

#ブール代数とは
ブール代数とは、「＋」と「―」と「￢(否定)」の3つの演算子、「0」と「1」の2つの値のみで構成された便利な数学で、和と積について交換法則・分配法則・単位元の存在・補元の存在が成り立ちます。

具体的には、

**交換法則**
(和) x + y = y + x
(積) x ・ y = y ・ x

**分配法則**
(和) x ・ (y + z) = (x ・ y) + (x ・ z)
(積) x + (y ・ z) = (x + y) ・ (x + z)

**単位元の存在**
(和) x + 0 = x
(積) x ・ 1 = x

**補元**
(和) x + ￢x = 1
(積) x ・ ￢x = 0

が成り立つということです。

この1を真(TRUE)、0を偽(FALSE)、+を論理和(または)、・を論理積(かつ)とみると、2値論理での真理表が完成することが分かりますよね。

##まとめ
今回伝えたかった＆面白いと思ったことは、
**「ブール代数を知っていれば真理表を簡単に導き出せる」**
ってことです。

興味がある方は、もっと調べてみてください。
シェルスクリプトで、とあるファイルがmasterブランチと差分があったら実行する

CIとかでとあるファイルに差分があった場合のみ特定の処理を実行したい時とかあると思います。
実際にあるか不明ですが、下記はカレントブランチとmasterブランチの間でdb/schema.rbに差分が合った場合
echoするシェルスクリプトの例になります。

```shell
if test $(git diff origin/master --name-only | grep db/schema.rb | wc -l) -ne 0; then                                                                                                                                                                                    
 echo 差分がありました
fi
```

### 解説
git diffに--name-onlyオプションをつけることで差分があったファイル名一覧を出すことができます。

```shell
git diff origin/master --name-only
```

それを差分を知りたいファイル名でgrepし、その結果の行数をwcコマンドで出しています。
行数が0だったら差分なし、0じゃない場合は差分があるものとして検知しています。
スクレイピング

#用途
ウェブサイト上のHTMLからデータを抜き出す処理

#使用例
例えばこのようなHTMLのサイトがあったとして

```
<ul>
    <li>TEST1</li>
    <li>TEST2</li>
    <li>TEST3</li>
  </ul>
```
TEST1
TEST2
TEST3
の値を取り出す事ができる

#必要なGem
###Mechanize

Mechanizeクラスが使えるようになる

Gemfileの最後の行に以下のコードを記述する

```Gemfile.
gem 'mechanize'
```
Gemfileに記述されたgemをインストールする

```
$ bundle install
```
#Mechanizeクラスのインスタンスを生成

スクレイピングするにはまず、Mechanizeクラスのインスタンスを生成する

```例.
agent = Mechanize.new
#Mechanizeクラスのインスタンスを生成して、agentへ代入
```
#webサイトのHTML情報を取得する
###getメソッド
getメソッドはMechanizeクラスのインスタンスメソッド
get(スクレイピングしたいウェブサイトのURL)

```例.
agent = Mechanize.new
page  = get("https://qiita.com/")
#QiitaのHTMLを取得
```
HTMLの文字列ではなく、ウェブサイトのHTMLの情報を持ったMechanize::Pageオブジェクトを取得
※オブジェクト
関連する変数(値)とメソッド(動作)をまとめて、そのまとまりに名前を付けたもの
NetlifyでBuildできません！

#### やろうとしていたこと：
Netlifyで自分のGithubのレポジトリにあるNuxt.jsのサイトをDeployしようとした。
#### 困った事：
エラーメッセージが出てきてビルドに失敗する。エラーメッセージもエラーメッセージだけで詳しくわからない。自分のおま環では問題なくビルドできる。
#### エラーメッセージ：

```
failed during stage 'building site': Build script returned non-zero exit code: 1
```

こんなエラーだけ愛想なく最後の方で出力されて、Buildに失敗。

#### 原因：Layoutのファイル内のモジュールファイルのimport命令のパスの書き方の誤りで迷子になっていたため、詳しいエラーメッセージも出ずにビルドに失敗していた。

当初はこんな書き方でした。

```
import sideNavigation from "@/components/nav/sideNavigation.vue";
```

@などをパスに使うには、設定する必要があるらしいです。[こちらを参照](https://qiita.com/soutarrr7/items/1b9a46df604ed78ec747)。でも面倒くさいので、相対パスでやります！

もう一つ。

ファイル名の大文字小文字の間違い。sideNavigationではなくSideNavigation.vueでした。通りでケースセンシティブなLinuxを使っているNetlifyでは通用しないはず。

```
import sideNavigation from "../components/nav/SideNavigation.vue";
```

これで1時間無駄にしました。でも無事に一件落着。コーヒーとアイスがうまい。
deviseを使ったログイン機能の実装の流れ(超おおまか+備忘録)

#1.deviseのgemをインストール (２回行う)
(1回目)

```
bundle install
```
その後、１回だけRailsサーバーを立ち上げ直す。

(2回目)

```
rails g devise:install
```

#2.userモデルを作成する

```
rails g devise user
```

モデル作成のコマンドを実行すると、マイグレーションファイルが作成される。そして、そのマイグレーションファイルを以下のコマンドで実行する。

```
rake db:migrate
```

そして、userテーブルを作成する。
https://gyazo.com/de5ffbed0dee23fd349bd014be895ce5

#3.ログインボタンの表示
ログイン時　→ 「ログアウト」ボタンと「投稿する」ボタンを表示させる。
非ログイン時　→　「ログイン」ボタンと「新規登録」ボタンを表示させる。

```html

<header class="header">
    <div class="header__bar row">
      <h1 class="grid-6"><a href="/">PicTweet</a></h1>
      <% if user_signed_in? %>
        <div class="user_nav grid-6">
          <%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
          <a class="post" href="/tweets/new">投稿する</a>
        </div>
      <% else %>
        <div class="grid-6">
          <%= link_to "ログイン", new_user_session_path, class: 'post' %>
          <%= link_to "新規登録", new_user_registration_path, class: 'post' %>
        </div>
      <% end %>
    </div>
  </header>
```

4行目のif文とuser_signed_in?メソッドを使って、「もしも、userがサインイン(ログイン)していたら、『ログアウト』ボタンと『投稿する』ボタンを表示させる」という処理を先に書く。次に、もしも、userがサインイン(ログイン)していなかったら、『ログイン』ボタンと『新規登録』ボタンを表示させる」という処理を後に書く。

#4.それぞれのボタンをクリックすると、それぞれの処理がなされる仕組みについて

「ログイン」ボタンや「新規登録」ボタンを押すと、ログインの処理や新規登録の処理がされる理由は、htmlのビューの画面に<%>(Rubyタグ)でlink_toメソッドを使って、ルーティングアクションを起こす仕組みを設定しているからである。link_toメソッドは、htmlのaタグに自動で読み変えてくれる便利なヘルパーメソッドである。

```html
<%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
 <a class="post" href="/tweets/new">投稿する</a>
```

```html
<%= link_to "ログイン", new_user_session_path, class: 'post' %>
<%= link_to "新規登録", new_user_registration_path, class: 'post' %>
```

link_toメソッドの書き方は、第1引数に文字列(ボタンの文字)、第2引数にルーティングに対するパス、第3引数にhttpメソッドを書く。

※ルーティングのパスを調べるには、rake routesのコマンドを実行する。

このような表記をすることで、ボタンをクリックすると、ルーティングにパスが送られて、コントローラがアクションするという流れができる。


以上の流れでログイン機能を簡単に実装することができる。
[Docker] Vueでaxiosを使うときにbaseurlを設定する方法

はじめまして、普段はproduct managerをやっているtatsukenと申します。
研修の一環でvue.js、expressを書くことがあったので、そのことを中心にまとめていきたいと思います
#はじめに
フロントエンドとapiをdockerで別々のサーバーとして立ち上げているときに、フロントからいちいちエンドポイントを指定するのが面倒だったので、いろいろ調べたmemo
# 何をするのか
vueからaxiosを用いてapiリクエストを行う際、いちいちURLを指定するのは面倒。

```sample.js
import Axios from 'axios'
async getAmount() {
        try {
            const res = await Axios.get(`http://localhost:8081/hoge`);
            console.log(res.data);
        } catch (error) {
            console.log(error);
        }
    }
```
このように毎回`http://localhost:8081/hoge`というようにurlを指定するのは面倒
できれば`/hoge`というように簡潔にエンドポイントを指定したい
## 解決策
まず`docker-compose.yml`でvueプロジェクトのdockerイメージ以下の`environment`に`VUE_APP_API_ENDPOINT: http://http://localhost:任意のport番号`を追加する

```docker-compose.yml
services:
  web:
   ...
    environment:
       VUE_APP_API_ENDPOINT: http://localhost:8081 //任意のport番号を指定
   ...
```
vueプロジェクト内の`main.js`に以下を追記

```main.js
import Axios from 'axios'
Axios.defaults.baseURL = process.env.VUE_APP_API_ENDPOINT;
```
これで以下のように簡潔に書けるようになります

```sample.js
import Axios from 'axios'
async getAmount() {
        try {
            const res = await Axios.get(`/hoge`);
            console.log(res.data);
        } catch (error) {
            console.log(error);
        }
    }
```

# 最後に
エンドポイントへのリクエストが増えれば増えるほどURLの指定は面倒になってくるので、よければ試してみてください。
なにか間違いなどありましたら指摘していただけると幸いです。
Excel VBAでネットワーク機器のconfig作成を効率化する

#したいこと
ルータのサブインタフェースを100個作りたいなど、ネットワーク機器などのconfig作成において繰り返しが必要な作業を自動で行うExcelVBAマクロを作ってみた。

##機能
・サブインタフェースなどの繰り返しconfigを作成可能。
・サブインタフェースの番号やVLANIDはインタフェースの数字をインクリメント可能
・インクリメントする値の初期値はconfig行毎で変えることも可能(サブインタフェースが1~でVLANIDが1001～など。)
・speed設定やno shutdownなどの共通configについてはそのままコピーする。


##シートの構成
<img width="1025" alt="キャプチャ.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404125/791da8b3-31a7-7a4f-c57f-303e6fa1a620.png">

シートはパラメータなどを入れるパラメータシートと生成されるconfigを吐き出す出力シートの2シートを作成した。
B行に繰り返したいconfigを書き、インクリメントしたい部分を$に置換する。インクリメントする値の初期値をC行に記入する。
E３セルに何ループさせるかを記入する。

##マクロ
短くシンプルにをモットーにガーっと書いてみた。
もうちょっときれいにしたい。

```vb
Sub configcreate()

Dim LOOPNUM As Integer
Dim CMDLINE As String
Dim INITVALUE As String
Dim NUMLINE As Integer
Dim OUTPUTCELL As Integer

Worksheets("出力シート").Columns("A:A").EntireRow.Clear '出力シートのA行をクリア
LASTROW = Worksheets("パラメータ").Cells(Rows.Count, 2).End(xlUp).Row
NUMLINE = LASTROW - 2
LOOPNUM = Worksheets("パラメータ").Cells(3, 5)
 'NUMLINE = Worksheets("パラメータ").Cells(6, 5) '行数を手打ちする場合はここで指定していた。現在は自動取得
OUTPUTCELL = 1  '出力1行目の行番号

For i = 1 To LOOPNUM    'ループ回数のfor
    For j = 1 To NUMLINE    '行数のfor
    CMDLINE = Worksheets("パラメータ").Cells(2 + j, 2).Value

        If InStr(CMDLINE, "$") > 0 Then
        INITVALUE = Worksheets("パラメータ").Cells(2 + j, 3).Value
            If i >= 2 Then
            k = i - 1
            INITVALUE = INITVALUE + k
            Else
            End If
            
        CMDLINE = Replace(CMDLINE, "$", INITVALUE)
        Else
    
        End If

        Worksheets("出力シート").Cells(OUTPUTCELL, 1).Value = CMDLINE
        OUTPUTCELL = OUTPUTCELL + 1
    Next
   
    
Next
    

End Sub

```

##マクロ実行結果
上記シート構成の画像のパラメータで回してみた。
想定通りの結果が出ているし、手作業で数時間かかる作業が１秒程度でできてしまった。
<img width="230" alt="キャプチャ2.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404125/1bf34288-a937-3378-4577-34b8e877a3f0.png">

##今後の課題
繰り返しだけでなく、config全般を自動生成するマクロを作りたい。
nVIDIA cudnnConvolutionBackwardFilterを調べる, ルーフラインモデル

# TL;DR

[cudnnConvolutionBackwardFilter()](https://docs.nvidia.com/deeplearning/sdk/cudnn-developer-guide/index.html#cudnnConvolutionBackwardFilter)を1000通りのパラメータでプロファイルし、
GTX1060のルーフラインモデルにプロットした。

![roofline.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88310/7c83bed2-610b-8fc7-79a7-276239cefa61.png)


多くの場合で、100Gflops以上の性能が出る一方で、極端に性能劣化するパラメータが存在することがわかった。
CUDNN_CONVOLUTION_BWD_FILTER_ALGO_FFTも一応使われることがあることがわかった。

# 評価対象

|Machine|nVIDIA GeForce GTX1060 3G|
|--:|--:|
|ピーク演算性能|3.469Tflops|
|ピークバンド幅|192GB/sec|
|CUDA|v10.0|
|cuDNN|v7.4|

# 評価条件(パラメータ)

1000通りのパラメータをランダムに生成。値域は下表の通り。

|パラメータ|値域|
|--:|--:|
|バッチサイズn|randint(1, 32)|
|入力チャネルci|randint(1, 20)|
|入力画像サイズHi|randint(1, 122)|
|入力画像サイズWi|randint(1, 122)|
|ストライドu(h方向)|randint(1, 7)|
|カーネルkernel_h|randint(1, 7)|
|カーネルkernel_w|randint(1, 7)|
|パディングpad_h|randint(0, 8)|
|パディングpad_w|randint(0, 8)|
|dilation_h|1固定|
|dilation_w|1固定|

※ アルゴリズムは、1000通りのパラメータそれぞれに対し最速のアルゴリズムを選択している。
→ [cudnnGetConvolutionBackwardFilterAlgorithm(..., CUDNN_CONVOLUTION_BWD_FILTER_​PREFER_FASTEST,...)](https://docs.nvidia.com/deeplearning/sdk/cudnn-developer-guide/index.html#cudnnGetConvolutionBackwardFilterAlgorithm)

# 評価式


* Performance [Gflops] = (演算回数) / (実行時間)
* Arithmetic intensity [GB/sec] = (演算回数) / (入出力データサイズ)
    * (演算回数): [c++でconvolution backward filterを愚直に書いて数え上げた](https://github.com/takata150802/cudnn_convBwdFilter/blob/f7335abf720d407f93f23e2b59d197644a613ace/prof_conv_bwd_filter.cc#L303)。mulとaddは2operationとしてカウントしている。
    * (実行時間)：[cudaEventElapsedTime()](https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__EVENT.html#group__CUDART__EVENT_1g40159125411db92c835edb46a0989cd6)を用いて[cudnnConvolutionBackwardFilter()](https://docs.nvidia.com/deeplearning/sdk/cudnn-developer-guide/index.html#cudnnConvolutionBackwardFilter)単体の実行時間を測定した。
    * (入出力データサイズ) = (n*ci*hi*wi + n*co*ho*wo + co*ci*kernel_h*kernel_w)*4 [Byte]


# 評価環境

![qiita_convBwdFilter_roofline.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88310/3df1c2bb-5d2e-2498-72fe-42ca8fbc93c8.png)


[ソースコード一式](https://github.com/takata150802/cudnn_convBwdFilter)
※ Cythonのsetpu.pyだけはいろいろハードコーディングしているのでリポジトリに入れていません。すみません。

# 参考文献

* ルーフラインモデルについて: 
  * https://toffee.jp/streaming/gpgpu/advanced_gpgpu/2015/advanced_gpgpu06.pdf
* Cythonによるc++のWarp方法について：
  * https://github.com/PySlurm/pyslurm/wiki/Strings-and-bytes-in-Cython
  * https://medium.com/@yusuken/calling-c-functions-from-cython-references-pointers-and-arrays-e1ccb461b6d8  
* CUDA Runtimeによる実行時間測定について:
  * https://qiita.com/syo0901/items/7ea3b8dfc01fd5cc2cf4
今回の環境でJqueryが動作しない。（Uncaught ReferenceError: $ is not defined）

今日一日どハマりしたので投稿します:joy:
急いで描いてるところもあるので、何か間違ってたら行ってください。
暇ができたら直しますｗ

記述があっているはずなのにイベントが発火しない。
さらにイベント発火直下にconsole.logを仕掛けても値すら返ってこない。
またブラウザのConsoleには以下のエラーが表示された。

Uncaught ReferenceError: $ is not defined

上記エラーを検索した結果、”””Rails5.1から仕様変更により、jQueryが標準実装されなくなった”””
とのことだったので以下の対応をした。
参照にした記事↓
https://qiita.com/Statham/items/372234e23749ff1f6bf8

最初にGemfileにgemを追加

```ruby:Gemfile
gem 'jquery-rails'
```
コンソールにてコマンド実行

```ruby:console
$ bundle install
```

以下のファイルに記述を追加。
※記載の順番に注意する！
→ require_tree . を最下層にしてrequire jqueryが一番最初に読み込まれるように記載。

```javascript:app/assets/javascript/application.js

//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
```

最後にJsファイルを記載するときは、最初の行に以下を記載する。
参照にした記事↓
https://qiita.com/hiroyayamamo/items/b258acbaa089d9482c8a

```javascript:hogehpge.js

$(document).on('turbolinks:load', function () { //この記述をしないと読み込まれない。
  $(‘セレクタ’).on('click', function () { //この記述は例です、今回の対応には関係ありません。
    console.log(this) 
    $(this).attr('class', '');
  })
});
```

とりあえず、今回はこの対応をしてエラーを解消しました。
もしかしたらいらない手順もあるかもしれません。
何かあれば言ってください！
VScodeでRust環境を整える

#概要
[実践Rust入門［言語仕様から開発手法まで］](https://gihyo.jp/book/2019/978-4-297-10559-4)のVScodeの設定の項目でちょっとハマったのでその備忘録です。

#rustup をインストール

```
curl https://sh.rustup.rs -sSf | sh
```

#PATHを通す（fishの場合）

```
set -U fish_user_paths $fish_user_paths $HOME/.cargo/bin
```


#VScodeの設定（MacOSの場合）

1. 拡張機能 [Rust（rls）](https://github.com/rust-lang/rls-vscode)をインストール（本ではここで設定が終わっているがこのままだと動かないので2.へ）
2. 設定から下記をセッティング

```
"rust-client.rustupPath": "/Users/${ユーザー名}/.cargo/bin/rustup"
```

以上
【Android】データの保存と共有について

#記事を移動しました。
https://zenn.dev/dd_sho/articles/222cd0cdf75c4b
今後は上記の記事で更新します。（2021/10/03）

Androidアプリにおけるデータの保存と共有についてのまとめ。

- 内部ストレージ（アプリPrivateなfileの保存領域）
- 外部ストレージ（SDカードなどの共有されたfileの保存領域）
- SharedPreferences（アプリPrivateなdataの保存領域）
- データベース（アプリPrivateな構造を持つdataの保存領域）

#### 他のアプリとの共有
>
Except for some types of files on external storage, all these options are intended for app-private data—the data is not naturally accessible to other apps.
If you want to share files with other apps, you should use the FileProvider API.

他のアプリとファイルを共有したい場合は、`FileProvider`を使う

>
If you want to expose your app's data to other apps, you can use a ContentProvider.

他のアプリとデータを共有したい場合は、`ContentProvider`を使う

## 内部ストレージ
- 他のアプリからはアクセスできない
- アプリ固有のデータを保存するのに適する
- Androidシステムがアプリ毎に固有のdirectoryを用意する
- アプリを削除すると内部ストレージ内のデータも削除される
- アプリを削除してもデータを残したい場合は`MediaStore`に保存する

内部ストレージの保存方法
https://developer.android.com/training/data-storage/files.html#WriteInternalStorage

### 内部キャッシュ
- 一時的なデータの保管に使う
- 内部ストレージの容量が少なくなるとAndroidシステムが削除するため、多くても1MBまでに留める

内部キャッシュの保存方法
https://developer.android.com/training/data-storage/files.html#WriteCacheFileInternal

## 外部ストレージ
- 他のアプリと共有したいデータの保存に使う（スクショやDLしたファイル等）
- アプリを削除しても保存される
- 常にアクセス可能とは限らない。SDカードなどがないと不可。
- 事前にアクセスできるか確認する必要がある。

外部ストレージの保存方法
https://developer.android.com/training/data-storage/files.html#WriteExternalStorage

## SharedPreferences
- データの規模が小さくて構造的なデータではないデータを保存するのに適する
 - 構造的なデータではない？→ `Boolean`、`Float`、`Int`、`Long`、`String`
- 取得と更新ができる
- key-value形式で記述されたxmlファイルとしてアプリ内に保存される
- SharedPreferencesのインスタンス生成時にxmlファイルの名前を任意に設定できる
- アプリを削除するとデータも削除される
- アプリ全体で共有されるSharedPreferencesとActivityに紐づくSharedPreferencesがある

SharedPreferencesの保存方法
https://developer.android.com/training/data-storage/shared-preferences.html

## アプリ内データベース
- AndroidではSQLite Databaseがサポートされている
- SQLiteで直接操作することもできるが、よりSQLiteが使いやすくなる**`Room`**というライブラリが用意されている

Roomの使い方（Google推奨）
https://developer.android.com/training/data-storage/room/index.html

SQLiteで直接操作する方法
https://developer.android.com/training/data-storage/sqlite.html


今回は以上です。
[備忘録]初めてGo言語を触ってみた（導入から初めてのHelloworldへ）

参考にさせて頂いた資料
[スターティングGo言語　著：松尾愛賀　翔泳社(Amazonページ)](https://www.amazon.co.jp/%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0Go%E8%A8%80%E8%AA%9E-%E6%9D%BE%E5%B0%BE%E6%84%9B%E8%B3%80-ebook/dp/B01FH3KRTI/ref=pd_ybh_a_1?_encoding=UTF8&psc=1&refRID=AV1FSYSP54M814C5SEWM)
[初めてのGo！インストールまで](https://qiita.com/inexp_eng4432/items/08dce692894c92ae08ee)
[Go + Visual Studio Codeの開発環境を作る](https://qiita.com/masakurapa/items/fb61b5fc07393059fb28)
[Goでpackageのimportのやり方(自分用に整理)](https://qiita.com/shiwork/items/a213b1af2da7f7f8ea22)

現在プログラミングスクールに通っているのですが、今回個人アプリ作成という期間があったので、前々から興味のあったGoに触れてみようと思いました。（まだ何作るか決めてません）
※尚自分向けアウトプットがメインなので、情報が間違いだらけの可能性ありと思ってください。
僕の頭の中ではこんな感じだよ位のイメージです。


最初は「スターティングGo言語」を見ながら、進めていたのですが、qiitaで調べた所、カリキュラムで既に導入していたHomebrewを使えば簡単にGoのインストールが出来るということでやってみました。

# Homebrewを使用したGoインストール
```console
$ brew install go    //Homebrewを使ってGoをインストール
$ go version        //Goが正常にインストールされていれば、これでバージョン確認が出来ます
```
# Hellowworldの流れ
```sample.go
package main    //Goでは1ディレクトリに１パッケージ、１パッケージの中に関数や変数は属すルールがあります。
                //なのでまずパッケージを宣言しないことには始まりません。
import(
  "fmt"         //importでは公式のパッケージや別ディレクトリで宣言したパッケージをimportします。
)               //別のパッケージの関数・変数を使いたい時にimportします。　　　　　　　　　　
                //今回はPrintln関数を使うのでfmtパッケージをimportしてます。               

func main(){              //func + 関数名(){　}　＝＝ 関数定義
  fmt.Println("hello world")  //Println("文字列")　=>　"文字列"の出力
}

```
```
go run sample.go  //Goの起動コマンド

// => hello workd
```


次にGOPATHを通します。
# GOPATHとは＆GOPATH設定

Goを使った開発をする際の作業ディレクトリのこと
（つまり"GOPATH"に設定したフォルダ内にアプリケーションのデータを置いて、コーディングしましょうということ）
これがないと使えるツールが少ないそうで、とにかく100％の力を使いたいので導入します。
設定するにあたり、環境変数を初めてイジりました（恐かったぁ）

GOPATHとして設定するフォルダの場所はどこでもいいらしい（という話をよく聞く）
自分は無難にホームディレクトリにgoフォルダ作成しようとしましたが、既にありました。
自分はVSCでコーディングしているのですが、HelloworldをGoで作成した際、Go書くときに便利なアドインあるよ！みたいな表示が出たので「install」押したんですよね。多分それの影響で生成されてる。
ということで既にあるフォルダをGOPATHに設定しました。

```console
vi ~/.bash_profile　             //環境変数をファイルを開きます
```


```.bash_profile

export GOPATH=$HOME/go           //ホームディレクトリ内goフォルダをGOPATHへ代入（定義） 
export PATH=$PATH:$HOME/go/bin   //ホームディレクトリ内goフォルダ内binフォルダをパスへ追加
```

これで取り敢えず準備できました。
次は基礎文法を勉強していきます。
（revelに挑んだんですが、ほぼ読めなかったので）
プロトアウトスタジオ 宿題質問(obniz x LineBot)

## はじめに
ProtoOutスタジオの宿題の質問です。

## やりたいこと
LINEbotに「#計測」とメッセージを送ると、超音波センサーで距離を測り、何ミリ離れているのかを通知させる。

## 実装
```javascript
'use strict';

const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');
const PORT = process.env.PORT || 3001;
const Obniz = require('obniz');

const config = {
    channelSecret: 'xxxx',
    channelAccessToken: 'xxxx'
};

const app = express();

var distance = 0;

app.get('/', (req, res) => res.send('Hello LINE BOT!(GET)'));
app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);
    Promise
      .all(req.body.events.map(handleEvent)) //引数のすべてのpromiseを集約する
      .then((result) => res.json(result));
});

const client = new line.Client(config); // LINEのclientを介してpushMessageなど処理する

function handleEvent(event) {
    // textメッセージ以外は処理しない
    if (event.type !== 'message' || event.message.type !== 'text') {
        return Promise.resolve(null);
    }

    const message = event.message.text
    if (message === '#計測') {
        hcsr04mesure(event.source.userId);
        return client.replyMessage(event.replyToken, {
            type: 'text',
            text: '計測中...'
        });
    }
}

// 測るとこ
const hcsr04mesure = async (userID) => {
    // process.on('unhandledRejection', console.dir);
    var obniz = new Obniz("xxxx-xxxx");
    obniz.onconnect = async () => {

        obniz.display.clear();

        var hcsr04 = obniz.wired("HC-SR04", {gnd:0, echo:1, trigger:2, vcc:3});
        distance = await hcsr04.measureWait(); 
        obniz.display.print(distance); //ここのdistanceは最新
        // 困り１
　　　　　// pushMsg(userID, distance) //ここでpushMsg する場合にuserIDがスコープ外になる
    }
    // 困り2
    pushMsg(userID, distance) //ここのdistanceは前回の値になっている
}

function pushMsg(userId, message) {
    return client.pushMessage(userId, {
      type: 'text',
      text: message,
    })
}
```

## 困り１
hcsr04mesureに引数としてuserIDを渡していますが、これをobniz.onconnect = async () => {} の中で利用する方法がわかりません。
obniz.onconnect = async (userID) => {} としても、userIDがobnizのデータで置きかわります。

## 困り2
obniz.onconnect = async () => {}の中の値の更新をどうやって待つのかがわからなかったです。
この状態のとき、測定してobnizのディスプレイに表示させてる値は最新の値ですが、
Lineにpushされる値は前回測定した値になります。