OmniAICreator/Qiita-1.07M huggingface dataset:
Unuboを使ってみた話

:warning: **Unubo はサービス終了したようです。(2020/06/13 追記)**  




先日[paiza開発日誌](https://paiza.hatenablog.com/entry/2019/09/11/Node_js%2CPython%2CRuby%E3%81%AA%E3%81%A9%E3%81%AEWeb%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E5%AE%8C%E5%85%A8%E7%84%A1%E6%96%99%E3%81%A7%E3%83%9B%E3%82%B9%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E5%8F%AF)で紹介されていた[**Unubo**](https://unubo.com)を使って簡単なサービスをデプロイしてみました。

このUnubo、まだ日本では全然広まっていないようですが、完全無料で「1 GB RAM, 1 CPU, and 500 MB storage」のサーバーを無料で複数利用できてめっちゃ便利です。
今後有料版のプランもリリースされていくようです（公式Twitterより）。

とりあえず今回はサンプルのflaskアプリケーションをデプロイしてみましょう。

#構成
Unubo Apps Python
Python 3.7
Flask 0.12

# 手順

## Unubo App の作成
<img width="1046" alt="スクリーンショット 2019-09-30 6.46.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342105/f459138d-6ace-e837-c18c-fe76f3d53022.png">
RegionはとりあえずAsiaで。

Data storeのドキュメントに
> Please not that databases can only be accessed from apps within the same region.

という記述があったので、DBも使うつもりの方はRegionを合わせましょう。

## デプロイ設定
### Repository
<img width="1046" alt="スクリーンショット 2019-09-30 6.52.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342105/51bb4367-7abe-58a4-1151-8e6f6b256d3c.png">
RepositoryのCONNECTからGitHubのデプロイ用リポジトリを選択しましょう。

### Deployment settings(Commands) 

アプリの起動用のコマンドを記述しましょう。
後述しますが、Unuboのサンプルコード通りとするなら

```sh
python app.py
```

gunicornを使用するなら

```sh
gunicorn app:app
```

としておきましょう。

### Secrets
<img width="1046" alt="スクリーンショット 2019-09-30 7.12.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342105/cdc03318-fdb7-fe40-a33f-f87c8b4bf4f5.png">

ここに環境変数をセットできます。
また、コード中で呼び出す際は *os.environ.get* を使用すればおkです。

```python
SECRET = os.environ.get('SECRET') # 'SECRET_VALUE'
SECRETS = json.loads(os.environ.get('SECRETS')) # ディクショナリで取得できる
```

Domains と Monitor は使用していませんm(._.)m

### コード
公式の[ドキュメント](https://docs.unubo.com/python/flask/)が、flaskのサンプルコードを公開してくれています。

```python:app.py(サンプル)
import os
from flask import Flask, render_template

app = Flask(__name__)

# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))

@app.route('/')
def hello():
    # return 'Welcome to Python on Unubo Cloud'
    return render_template('index.html')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=port)
```

```txt:requirements.txt(サンプル)

Flask==0.12
```

コードをGitHubにpushして、Deployボタンを押下すれば{app名}.unubo.appにデプロイされます。

# 試しに作ったもの
[IPAカウントダウン](https://ipa-countdown.unubo.app/)
IPA試験までの時間をカウントダウンをしています。
デザイン手抜きです（UIKit使い方よくわかってない）。
また実装したAPIをGoogleAppsScriptから叩いて、[IPAカウントダウンbot](https://twitter.com/IPAbot1)が7時台、12時台、19時台にカウントダウンツイートをするようにしています。

# 感想
デプロイでそんなにつまることもなかったしUIもすっきりしていて使いやすいです。
これを無料で使えるのは素晴らしい。
公式Twitterがエゴサしっかりしていて、めっちゃリプライくれるのも好感もてます。
Terraformの「ここはvariable使えないのか...」となった所

[Terraform](https://www.terraform.io/)ってすごいツールですよね。ブラウザコンソールとかで一個ずつちまちま作らずともコマンド一発で一連のインフラができてしまいます。
ソースを落としてきてterraform.tfvarsの `variable` を編集して `terraform init`, `terrafrom apply` で構築完了するように設計しようとしていたのですが、ところどころvariableが使えない箇所でハマったので備忘録としてメモ。

TL;DR
======
* プロバイダーバージョンの指定にはvariableは使えない。
* バックエンドの定義にはvariableは使えない。
* `terraform init` で必要な定義はvariableで指定できない。

プロバイダーのバージョン指定
=====================
[issueでのコメント](https://github.com/hashicorp/terraform/issues/17211#issuecomment-363488498)によると

```tf
provider "aws" {
  version = "${var.aws_provider_version}"
  region  = "${var.aws_region}"
}
```

このようにproviderのversionのvariableによる定義はダメらしく、

```tf
provider "aws" {
  version = "~> 2.0"
  region  = "${var.aws_region}"
}
```

としないといけない仕様らしい。ここは大人しくハードコーディングにて対応しました...。

バックエンドの定義
=============
`terraform.apply` をすると構築したインフラの状態がJSON形式で記された `terraform.tfstate` が作成されるが、バックエンドを定義すると、このtfstateファイルをS3バケットなどに保存して管理できる。

```tf
terraform {
  backend "s3" {
    bucket = "${var.bucket_name}"
    key    = "${var.key}"
    region = "${var.region}"
  }
}
```

といきたいところだがこれもやはりダメらしい...

### 解決策1: 大人しくハードコーディング

一番簡単な解決策。

```tf
terraform {
  backend "s3" {
    bucket = "mybucket"
    key    = "path/to/terraform.tfstate"
    region = "us-east-1"
  }
}
```


[この人](https://github.com/hashicorp/terraform/issues/13022#issuecomment-438706246)が「なんでこれハードコーディングせなあかんねん」と代わりに文句を言ってくれている。

### 解決策2: `terraform init` 実行時に `-backend-config` にて指定

> Command-line key/value pairs: Key/value pairs can be specified via the init command line. Note that many shells retain command-line flags in a history file, so this isn't recommended for secrets. To specify a single key/value pair, use the -backend-config="KEY=VALUE" option when running terraform init.

[ドキュメント](https://www.terraform.io/docs/backends/config.html)に書いてあるように `terrarfom init` 実行時に `-backend-config` で指定することが可能。

```main.tf
terraform {
  backend "s3" {
  }
}
```

としておいて

```shell
$ terraform init -backend-config="bucket=backend.bucket" \
                 -backend-config="key=path/to/terraform.tfstate" \
                 -backend-config="region=us-east-1"
```

のように `terraform init` を実行。

### 解決策3: 別ファイルに設定を書く

[issueのコメント](https://github.com/hashicorp/terraform/issues/17288#issuecomment-439103022)で言われているように変数を別のファイルに書いておいて `terraform init` 時に読み込ませる方法がある。

```main.tf
terraform {
  backend "s3" {
  }
}
```

```production.tfbackend
bucket = "mybucket"
key    = "path/to/terraform.tfstate"
region = "us-east-1"
```

としておいて

```shell
$ terraform init -reconfigure -backend-config=production.tfbackend
```

のように実行する。

まとめ
======
variableの値の反映は `terraform plan` や `terraform apply` などのタイミングで行われるようで `terraform init` で必要な定義はvariableで指定できず、ハードコーディングするかそれなりの対応をしないといけない。
【簡単!!】VirtualBoxのホストOSとゲストOS(仮想環境)間のマウスポインターの移動方法

## 1.事前知識
- [【画面あり】VirtualBoxでCentOSの仮想環境を作る方法](https://qiita.com/ryome/items/e55caf5bed345f95451d)

事前知識として、上記リンクの内容が必要です。

## 2.Guest Additions CDイメージの挿入
1. メニューの `デバイス＞Guest Additions CDイメージの挿入` をクリックします。
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/151fe2cc-b641-53cd-b74c-6fdce4cdbc29.png)
2. `実行する` をクリックします。
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/678dbd61-e2f9-8dc8-7920-a8b65ce01bab.png)
3. `パスワード` を入力し、 `認証` をクリックします。
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/642a888b-a5af-e3dc-1c52-afc367c9c5b1.png)
4. コマンドラインに `Press Return to close this window...` が表示されたら閉じます。
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/3e0ed33a-7ca1-2374-236a-1789f665c190.png)
5. デスクトップに表示されたディスクアイコンを右クリックして、 `取り出す` をクリックし、再起動して終了です。
![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/6a812aaf-2d3c-3406-373c-802075fa4310.png)


## 3.まとめ
### Guest Additions CDイメージの挿入

1. メニューの `デバイス＞Guest Additions CDイメージの挿入` をクリックします。
2. `実行する` をクリックします。
3. `パスワード` を入力し、 `認証` をクリックします。
4. コマンドラインに `Press Return to close this window...` が表示されたら閉じます。
5. デスクトップに表示されたディスクアイコンを右クリックして、 `取り出す` をクリックし、再起動して終了です。
CentOSでネットワークを有効化する方法

## 1.事前知識
- [【画面あり】VirtualBoxでCentOSの仮想環境を作る方法](https://qiita.com/ryome/items/e55caf5bed345f95451d)

事前知識として、上記リンクの内容が必要です。

## 2.ネットワークに繋がらない

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/94f524ef-60d9-5a59-ce8c-df95f2eefdde.png)

- 画像のようにネットワークに繋がらないときの対処法です。

## 3.ネットワーク接続

1. 右上の `電源アイコン` から `ネットワークの接続` をオンにします。
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/82ac5ed4-5fad-2731-c409-e659868ab3c0.png)
2. 画像のように `検索` できたら成功です。
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/46d7a846-a5ec-efb2-f18a-1af6b846b756.png)


## 4.まとめ
### ネットワーク接続

1. 右上の `電源アイコン` から `ネットワークの接続` をオンにします。
2. 画像のように `検索` できたら成功です。
RN(Expo)でQR表示・・・（2020年1月更新）


常に事情が変わるので随時更新する記事にします。

## 2020年1月8日追記

以前書いた時はiOS, Androidの両方で動くライブラリがなくて困った。
ダメ元で、

[react-native-custom-qr-codes-expo](https://github.com/eddyoc/react-native-custom-qr-codes-expo)

というのを試したらiOS, Androidで動いた。

### インストール

react-native-svgが必要（たぶん私がexpo環境だから）。

```bash
npm install --save react-native-custom-qr-codes-expo react-native-svg
```

### 簡単な実装

ロゴとかも入れられる。が、グラデーション機能とかはうまく動かなかった。。。

```js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { QRCode } from 'react-native-custom-qr-codes-expo';

export default function App() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <QRCode
        content="12345"
        logo={require('./assets/logo.png')}
        logoSize={50}
        color="#aaa"
        size={200}
      />
    </View>
  );
}
```


## 以下オリジナル

RNでQRを表示する方法は[以前にも書いた](https://qiita.com/zaburo/items/0f81d8568e577f03da1d)のですが、どうやら[そのライブラリ](https://github.com/cssivision/react-native-qrcode)は最新のiOS環境では動作しなくなったようです（メンテもしないそうです）。

で、[react-native-qrcode-svg](https://github.com/awesomejerry/react-native-qrcode-svg)というモジュールがあったのでそちらを利用し無事表示できたのですが、それはAndroid環境では動作しないようでした（[数日前issue](https://github.com/awesomejerry/react-native-qrcode-svg/issues/67)が上がっていたので、最近動かなくなったのかもしれません）。

で、幸か不幸か、以前のモジュールreact-native-qrcodeはAndroid環境でも動作するようなので、Platformにより分岐させてモジュールを使い分けるしかないかなと。。。

以下、テストコード。

JSX内で分岐させるために即時関数を利用しています。

```js:App.js
import React from 'react';
import { StyleSheet, Text, View, Platform } from 'react-native';
import QRCodeSVG from 'react-native-qrcode-svg'; //iosで動く
import QRCode from 'react-native-qrcode';//androidで動く

export default class App extends React.Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                {
                    (() => {
                        if (Platform.OS === 'ios') {
                            return (
                                <QRCodeSVG
                                    value="12345"
                                />
                            );
                        } else {
                            return (
                                <QRCode
                                    value="12345"
                                />
                            );
                        }
                    })()
                }
            </View>
        );
    }
}

```

## 参考

* https://www.yoheim.net/blog.php?q=20180409
Railsのsecrets.ymlとは何者なのか

## はじめに
railsで開発しているときに、よく目にする（した）secrets.ymlの役割がよくわかっていなかったので備忘録的にまとめる。

(rails5.2以降は廃止され、credentials.yml.encというファイルになった → [Rails5.2からsecrets.yml*が廃止されcredentials.yml.encに統合されるよ](https://qiita.com/daichirata/items/da40e205d273ae69fcfc))

## まとめ
- secrets.ymlは暗号化通信のために利用される設定ファイル
- secret_key_baseを設定し通信を暗号化する
- production環境で他人に見られても問題ない

## そもそも
- .ymlとはYAMLファイルの拡張子である。
- YAMLファイルは設定を簡潔に記述するためのファイル  
 (参考→[YAMLとは何か？ - いつもRailsの設定ファイルで出てくるやつの正体](https://qiita.com/Yama-to/items/587544993fb62610528a))

YAMLファイル自体が動くのではなく、プログラムが読み込んで使う。

## 役割
とりあえずrails newしてみる。

するとこんな感じになっている
<img width="655" alt="スクリーンショット 2019-09-30 5.38.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/388054/de75a67c-e3fc-1d26-a87a-aae23745dcef.png">

どうやらsecret_key_baseを設定しているようだ。

secret_key_baseの役割は、Cookieの暗号化など。
secret_key_baseの値が漏れてしまうと復号されてしまうので、production環境では環境変数にsecret_key_baseの値を設定し、secrets.ymlで環境変数を指定する。
production環境においては環境変数が漏れなければ問題ないため、secret.ymlをgitignoreする必要はない。

(参考→
[Railsのsecret_key_baseを理解する（翻訳）
](https://techracho.bpsinc.jp/hachi8833/2017_10_24/46809)[secret_key_baseからのhack
](https://qiita.com/8398a7/items/10e15746034d74232dfe))

以上です。
DOMCrawlerの使い方

### はじめに
Laravelを使用している時にスクレイピングを行ないました．
その際に使ったDOMCrawlerの使い方をまとめました．

### インポート
```
use Symfony\Component\DomCrawler\Crawler;

$html = <<<'HTML'
<!DOCTYPE html>
<html>
    <body>
        <p class="message">1番目のP</p>
        <p>2番目のP</p>
        <p>3番目のP</p>
    </body>
</html>
HTML;

//HTMLの文字列をCrawlerクラスに渡す．
$crawler = new Crawler($html);
```

### フィルタリング

### XPathを用いる方法
```
$crawler->filterXPath('html/body/p')
```

### CSSセレクタを用いる方法
```
$crawler->filter('body > p')
```

### Nodeを選択する

0番目のp要素の取得する

```
$crawler->filter('body > p')->eq(0);
```

最初のp要素の取得する

```
$crawler->filter('body > p')->first();
```

最後のp要素の取得する

```
$crawler->filter('body > p')->last();
```

最初のp要素の兄弟にあたるものを取得する

```
$crawler->filter('body > p')->first()->siblings();
```

例えば下記のようなHTMLがあった場合．
取得するのは```<p>2番目のP</p>と<p>3番目のP</p>```を取得する

```
<body>
    <p class="message">1番目のP</p>
    <p>2番目のP</p>
    <p>3番目のP</p>
</body>
```

選択したノードの子供の要素を取得する．

```
$crawler->filter('body')->children();
$crawler->filter('body')->children('p.message');
```

選択したノードの親の要素を取得する．

```
$crawler->filter('body > p')->parents();
```

### ノードの値を取得する

ノードの名前を取得する

```
$tag = $crawler->filterXPath('//body/*')->nodeName();
```

例えば下記のようなHTMLがあった場合．
取得するのは1番最初に該当するノードの名前を取得する．なので今回の場合は``` p ```が返却される.

```
<body>
    <p class="message">1番目のP</p>
    <p>2番目のP</p>
    <p>3番目のP</p>
</body>
```

ノードの値を返す

```
$message = $crawler->filterXPath('//body/p')->text();
```

クラスの名前を取得する

```
$class = $crawler->filterXPath('//body/p')->attr('class');
```

複数のノードの値を一度に取得する方法

```
$nodeValues = $crawler->filter('p')->each(function (Crawler $node, $i) {
    return $node->text();
});
```

### 画像の取得

```
// selectImageの中にはalt属性の値を指定する
$image = $crawler->selectImage('イラスト1')->image();
```
【centOS】snapでinstallしようとしたところopenssl.cnfが開けないと言われたのでどうにかした

# td;tl
VPSのcentOSでsnapを用いてmicrok8sをインストールしようとしたところ、

```shell

$ sudo snap install microk8s --classic
︙

+ '[' -f /var/snap/microk8s/920/certs/ca.crt ']'
+ openssl req -x509 -new -nodes -key /var/snap/microk8s/920/certs/ca.key -subj /CN=10.152.183.1 -days 10000 -out /var/snap/microk8s/920/certs/ca.crt
WARNING: can't open config file: /usr/lib/ssl/openssl.cnf
Unable to load config info from /usr/lib/ssl/openssl.cnf
-----)

︙

```
このようなエラーが出たので対処した.
かんたんに漁ったけど見つからなかったのでpost

`openssl.cnf`が無いらしいので確認


```shell
$ cat /usr/lib/ssl/openssl.cnf
cat: /usr/lib/ssl/openssl.cnf: そのようなファイルやディレクトリはありません
```
純粋にファイルが無いっぽい

```

$ sudo find / -name openssl.cnf
/etc/pki/tls/openssl.cnf
/var/lib/snapd/snap/core/7713/etc/ssl/openssl.cnf
/var/lib/snapd/snap/core/7713/usr/lib/ssl/openssl.cnf

```

`/etc/pki/tls/`にそれっぽいのがあったので

```shell
$ sudo less /etc/pki/tls/openssl.cnf
```

中身を見てみたらそれっぽかった.




```shell
 sudo ln -s /etc/pki/tls/openssl.cnf /usr/lib/ssl/openssl.cnf
```
雑にリンクを貼る

```shell
︙

microk8s (edge) v1.16.0 from Canonical✓ installed
```
うむ。





# 環境
```shell
# cat /etc/centos-release
CentOS Linux release 7.7.1908 (Core)

$ snap version
snap    2.39.2-1.el7
snapd   2.39.2-1.el7
series  16
centos  7
kernel  3.10.0-957.27.2.el7.x86_64

$ openssl version
OpenSSL 1.0.2k-fips  26 Jan 2017
```


snapが/etc/pki/tlsにopenssl.cnfが生成されるパターンまで
拾いきれてない感じなんですかねこれは


こういうのissue立てたほうがいいんだろうけどイマイチわからない、教えて強い人！

というかそもそもの話、microk8sを動かすならubuntuのほうがいいです。
AVCaptureSessionDidStopRunningが呼ばれるタイミング

AVCaptureSessionのstopRunning()を呼ぶと、`AVCaptureSessionDidStopRunning`が発信される。これは普通の挙動。
しかし、AVCaptureSessionがrunnning状態で開放された時は呼ばれないので注意。

以下検証コード

```swift
import UIKit
import AVFoundation

class ViewController: UIViewController {

  private var token: NSObjectProtocol? = nil
  var camera: Camera? = Camera()
  
  override func viewDidLoad() {
    super.viewDidLoad()
    let nc = NotificationCenter.default
    token = nc.addObserver(forName: NSNotification.Name.AVCaptureSessionDidStopRunning, object: nil, queue: .main) { (_) in
      print("stop")
    }
    camera?.start()
    camera = nil
  }
}

class Camera: NSObject {
  lazy var session: AVCaptureSession = .init()
  lazy var input: AVCaptureDeviceInput = try! AVCaptureDeviceInput(device: device)
  lazy var device: AVCaptureDevice = AVCaptureDevice.default(for: .video)!
  lazy var output: AVCaptureVideoDataOutput = .init()
  
  override init() {
    super.init()
    session.addInput(input)
    session.addOutput(output)
    output.setSampleBufferDelegate(self, queue: .main)
  }
  
  func start() {
    session.startRunning()
  }
  
  func stop() {
    session.stopRunning()
  }
}
extension Camera: AVCaptureVideoDataOutputSampleBufferDelegate {
  func captureOutput(_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
    
  }
  
  func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
    
  }
}
```
DOM を生成する便利関数

Vanilla JS で DOM を生成したいときに便利な関数を作りました。

# 定義

```javascript:h.js
/**
 * @template {keyof HTMLElementTagNameMap} K
 * @param {K} tagName
 * @param {Record<string, any>} options
 * @param {(string | Node)[]} children
 * @returns {HTMLElementTagNameMap[K]}
 */
function h(tagName, options = {}, ...children) {
  const element = document.createElement(tagName);
  element.append(...children);
  return Object.assign(element, options);
}
```

```typescript:h.ts
function h<K extends keyof HTMLElementTagNameMap>(
  tagName: K,
  options: Record<string, any> = {},
  ...children: (string | Node)[]
): HTMLElementTagNameMap[K] {
  const element = document.createElement(tagName);
  element.append(...children);
  return Object.assign(element, options);
}
```

# 使い方

```javascript
const hr = h('hr');
const img = h('img', { src: 'image.png', alt: 'イメージ' });
const a = h('a', { href: '/' }, 'TOPへ');

const div = h('div', { className: 'sample' }, img, hr, a);
```

```javascript
const records = [
  [ 'Alice', 'alice@example.com' ],
  [ 'Bob', 'bob@example.com' ],
  [ 'Carol', 'carol@example.com' ],
];

h('table', {}, ...records.map(r =>
  h('tr', {}, ...r.map(v =>
    h('td', {}, v)
  ))
));
```

書き捨てのスクリプトなんかにどうぞ。
Error response from daemon: No build stage in current context

# 初歩的なミスの解決に少し手間取ったので一応メモ

コレを

```docker:正しいDockerfile
    FROM ubutnsu
    LABEL MAINTAINER xxxxxx
    ~~~ 略 ~~~
```

こう書いてしまっていた

```docker:誤ったDockerfile
    MAINTAINER xxxxxx # 署名はFROMより下にしないといけない
    FROM ubutnsu
    ~~~ 略 ~~~
```

ただそれだけが原因でした
fishでコマンドが青色になって見にくい時の対処法

以下の画像のように、コマンドが青色で表示されるのだが、発色が悪いディスプレイだと見にくい。
白に変更したかったのだが、意外と苦戦したのでメモしておく。

<img width="119" alt="スクリーンショット 2019-09-30 2.31.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86632/7ef06e45-b7ac-41ae-29f8-e89be98bd14f.png">

# 対処法
`~/.config/fish/config.fish` に以下を追加する。

```shell
set fish_color_command white
```

`white` の部分を `"#FFF"` のように設定することもできる。
プラグインで入れているカラーテーマが設定している色だと勘違いして時間をとられた。
Airflowのtrigger ruleとハマりどころ

Airflowなどのワークフローエンジンのメリットに、処理の依存関係を制御出来ることがあります。
この記事では、Airflowにおける依存関係の設定（の一つ）、trigger ruleについて説明します。

# trigger ruleとは
タスク間の依存関係のルールです。

Airflowでは、デフォルトでも

```
task1_1 >> task2
task1_2 >> task2
```

のように書くと、task1_1とtask1_2の**両方**が**成功**した時にtask2を実行するように、タスク間の依存を設定出来ます。

trigger_ruleをタスクに設定することで、
* **両方**が**失敗**
* **いずれかが**が**成功**
など、依存の仕方を指定出来るようになります。


# 用語
* Operator
* Sensor
* タスク
* タスクインスタンス
* upstream/downstream


## 設定の仕方
[公式ドキュメント](https://airflow.apache.org/concepts.html)例の、

```

join = DummyOperator(task_id='join', dag=dag, trigger_rule='none_failed')
```
みたいな感じで、Downstream側のタスクに設定します([BaseOperator](https://github.com/apache/airflow/blob/5f9ab7a1d5cd4540e953005d43898be08ed56d60/airflow/models/baseoperator.py)で定義されているので、どのオペレータでも設定出来ます)。
ちなみに、文字列でも定数（airflow.utils.TriggerRule）でも設定出来ます。


# ルールの一覧
1.10.5の時点で8種類あります。デフォルトはall_successです。

https://airflow.apache.org/concepts.html#trigger-rules

    all_success: (default) all parents have succeeded

    all_failed: all parents are in a failed or upstream_failed state

    all_done: all parents are done with their execution

    one_failed: fires as soon as at least one parent has failed, it does not wait for all parents to be done

    one_success: fires as soon as at least one parent succeeds, it does not wait for all parents to be done

    none_failed: all parents have not failed (failed or upstream_failed) i.e. all parents have succeeded or been skipped

    none_skipped: no parent is in a skipped state, i.e. all parents are in a success, failed, or upstream_failed state

    dummy: dependencies are just for show, trigger at will

名前で大体わかりますが、具体的な挙動は[tirgger_rule_dep](https://github.com/apache/airflow/blob/f14947cfb36e564299c0ef2cf191ea309759c4ed/airflow/ti_deps/deps/trigger_rule_dep.py)で確認出来ます。
（なお、all_doneはupstreamの[skipもdoneとみなします](https://stackoverflow.com/questions/41670256/what-is-the-difference-between-airflow-trigger-rule-all-done-and-all-success)）



# 落とし穴
trigger_ruleはタスクが実行されるかを指定しますが、**それだけ決まるわけではない**です。

「タスクインスタンス動かない…」みたいになる人が多いらしく、[ドキュメントにもFAQ](https://airflow.apache.org/faq.html#why-isn-t-my-task-getting-scheduledには)があり、

* depends_on_past（前のDagRunのタスクインスタンスが成功に依存）
* pool
* タスクの並列度の設定（concurrency）
* start_date

などの例が書かれています。

## ハマった例：Sensorのsoft_fail
FAQに無いハマりどころに、**upstreamのタスクインスタンスがdownstreamのタスクインスタンスをskipにする**ケースがあります。
この場合、**downstreamのタスクをtrigger_ruleをall_doneにしても**実行されないようです。

私がハマった、Sensorのsoft_failオプションの例です：

1. AirflowのSensorは、センサー対象(Pub/Subなど)が条件(具象クラスのpokeメソッド)を満たすまで待ちます
1. timeoutオプションが設定されていると、一定時間内に条件を満たさなければタスクインスタンスはfailします
1. soft_failオプションがtrueの場合、タイムアウトした場合、タスクインスタンスはskipします
1. soft_failオプションはSensor自身だけではなく、**downstreamのタスクもskip**します

[コード的にはここらへんです。](https://github.com/apache/airflow/blob/61190c30df17772ec84e9cb8dc4370c9394fd92c/airflow/sensors/base_sensor_operator.py#L114)

```
                    self._do_skip_downstream_tasks(context)
```



# 関連リンク
* [FAQ](https://airflow.apache.org/faq.html)
* [Pitfall](https://cwiki.apache.org/confluence/display/AIRFLOW/Common+Pitfalls)
RailsのアプリをHerokuにデプロイした時のエラー解決(自分用メモ)


##環境
Ruby 2.6.1
Ruby on Rails 5.2.3

_2019.9.30現在_

##問題1
Herokuにデプロイする際に言われたこと

```
ターミナル

remote:  !
remote:  !     Could not detect rake tasks
remote:  !     ensure you can run `$ bundle exec rake -P` against your app
remote:  !     and using the production group of your Gemfile.
remote:  !     Activating bundler (2.0.1) failed:
remote:  !     Could not find 'bundler' (2.0.1) required by your /tmp/build_d954aa4ae56d81a84f1107b3fad374d5/Gemfile.lock.
remote:  !     To update to the latest version installed on your system, run `bundle update --bundler`.
remote:  !     To install the missing version, run `gem install bundler:2.0.1`
remote:  !     Checked in 'GEM_PATH=/tmp/build_d954aa4ae56d81a84f1107b3fad374d5/vendor/bundle/ruby/2.6.0', execute `gem env` for more information
remote:  !     
remote:  !     To install the version of bundler this project requires, run `gem install bundler -v '2.0.1'`
remote:  !
```

これでherokuにデプロイが失敗。
Herokuが　_bundler 2.0.2_　に対応していない？？
ここがちょっとわかりませんでしたが2.0.1をインストールと言われたので

```
$ bundle install -v 2.0.1 
```
_bundler 2.0.1_ をインストール

もう一度herokuに

```
$ git push heroku master
```

しかし、同じエラーが起きて失敗
結局、bundler _1.17.3_ を使いました。

```
$ bundle _1.17.3_ install
```

bundler 1.17.3 だといけました！

※もしかしたら、Gemfile.lockで2.0.1なのに、2.0.2だったのが問題だったのかもしれないです。
わかるかたコメントください！！





##問題２

```
remote:  !
remote:  !     Precompiling assets failed.
remote:  !
```

この上の方にエラー文がありました。

```
remote:        Uglifier::Error: Unexpected token: name (w). To use ES6 syntax, harmony mode must be enabled with Uglifier.new(:harmony => true).
```

従って

```ruby:config/environments/pruduction.rb
config.assets.js_compressor = :uglifier
　　　　　　　　　　　　　　　　　　↓↓↓↓↓↓
config.assets.js_compressor = Uglifier.new(harmony: true)
```
イコールの後ろを変える。
_Uglifierの「U」は大文字なのとシンボルは要らなくなるので注意_

さらに上と同じファイル内に

```ruby:config/environments/pruduction.rb
config.assets.initialize_on_precompile = false
```

を追加します。

```
$ git push heroku master
```

```
remote:        https://アプリ.herokuapp.com/ deployed to Heroku
```
これにてアプリがデプロイできました！！

##追記
```
remote:        Sass::SyntaxError: Invalid CSS after "}": expected "}", was ""
```
一番最後にこのエラーが出てしまいましたがscss内でどこか "}" が抜けていました、、、

##参考
https://qiita.com/pokohide/items/017afa5be3b7dc200a8d
https://qiita.com/Kohei_Kishimoto0214/items/1da88849eb298de7f9a6
Visual studio codeでタイムアタックする

#コードはこちら
https://github.com/MingSiro71/vsc_timeAttack

#何を言っているのかわからない
な…　何を言ってるのか　わからねーと思うが
おれも何をしたのかわからなかった…

jsファイルをVisual Studio Codeで開いて指示に従ってコードを書き換えると
htmlをブラウザで開いたときにコンソールにタイムが表示されるんだ…

ショートカットキーを駆使してベストスコアを叩き出すんだ

コードの書き換え方は全てコードの中に書いてある
正しく書き換えればボーナスポイントがもらえるはずだ
ただしボーナスポイントは手計算で頼む…!

#ショートカットキーとギミック
##開始前に：拡張機能のインストール

```vsc_timeAttack.js
// FIRST OF ALL, INSTALL EXTENSION "Insert Date String" from MARKETPLACE!
// ON STARTING TIME ATTACK, PRESS "ctrl + shift + i" TO SET TIMESTAMP TO VARIABLE 'start'!

// ARE YOU READY?

// GO!
const start = "";
```

このコードで遊ぶためにはInsert Date Stringが必要です。
拡張機能でstartの値にショートカットキーで時間をいれられるようになります

##マルチカーソル
同じ文字をまとめて選択する機能です。
Win: ctrl + D
Mac: command + D または command + shift + L

コード中のゴミを取り除くと青い天の川が見えるようになります。
![スクリーンショット 2019-09-30 0.54.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271466/32fd7a7e-789d-acf0-8a26-2955489867f7.png)

##カッコの最後へ飛ぶ
()や{}の片割れから反対側の片割れに飛ぶことができます。
Win: ctrl + shift + ¥(or \\)
Mac: command + shift + ¥(or \\)

##次の単語に飛ぶ
Win: ctrl + →
Mac: option + →

世界一長いとされる英単語が無駄に並んでいる中から必要な部分をコピペします。

```vsc_timeAttack.js
  // supercalifragilisticexpialidocious supercalifragilisticexpialidocious supercalifragilisticexpialidocious supercalifragilisticexpialidocious supercalifragilisticexpialidocious font-size:48px;color:red; supercalifragilisticexpialidocious supercalifragilisticexpialidocious
  const s = "";
```

正しくいれると2つの"A"が同じ色と大きさになります。

![スクリーンショット 2019-09-30 1.04.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271466/7e614b11-a42a-f48f-cf60-c74bbb220ec2.png)

##ファイルの末尾へ飛ぶ
Win: ctrl + end
Mac: command + ↓

##文字列の検索
Win: ctrl + F
Mac: command + F

##検索文字列へジャンプ
(検索ウィンドウに文字が入っていて、カーソルがコードにある状態で)
Win: ctrl + G
Mac: command + G

コードの最終行からコピペして指定の場所へ貼り付けると牛さんが登場します。

…牛さん？
https://ja.wikipedia.org/wiki/APT

![スクリーンショット 2019-09-30 1.15.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271466/a0032922-833c-7321-9b9d-3420038e2cc5.png)

##行コピー
(範囲選択をしない状態で)
Win: ctrl + C
Mac: command + C

##すぐ下に空白行を作って移動
Win: ctrl + enter
Mac: command + enter

アルパカの首をコピペして相手のアルパカよりも首を長くしましょう。
結果はあなたのアルパカが左、相手のアルパカが右に表示されます。

![スクリーンショット 2019-09-30 1.20.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271466/9968f784-c423-2b3c-fa77-b4465c27e77f.png)

##行削除
Win: ctrl + shift + K
Mac: command + shift + K
相手のアルパカの首を行削除して短くしましょう。
結果はあなたのアルパカが左、相手のアルパカが右に表示されます。

![スクリーンショット 2019-09-30 1.23.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271466/f08cfbe5-b0e7-a52c-0193-1d6aa86e472d.png)

##ゴール！

```vsc_timeAttack.js
// IF YOU FINISH THE CHALLENGE, PRESS "ctrl + shift + i" TO SET TIMESTAMP TO VARIABLE 'finish'!
// IF NOT, SEARCH AND GO TO "alpaca".
const finish = "";
```
最後はfinishを検索して飛んでタイムを入れたらゴールです！

#やってみた
![スクリーンショット 2019-09-30 1.41.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271466/66c7e0bd-c6da-085e-b85e-f29a8d78a081.png)

おせえ！
LaraDock環境でApacheコンテナ起動時にポートエラーが発生した時の対処方

# はじめに
LaraDock環境で、Apacheのコンテナを起動しようとした時に、ポートエラーが発生した時の対処方法を記載する。

# エラー文
laradockフォルダ下で、以下のコマンドを実行する。

```:コマンド
docker-compose stop apache2
```
<br>
上記のコマンドを実行すると、下記のようなエラー文が出力されることがある。
エラー文１、apache関係でエラーが発生していることが予想がつきます。

```:エラー文１
Recreating laradock_apache2_1 ... error
```
次に、下記のようなエラー文２が発生していたら、そのポート番号が他で使ってるので使えないよって意味です。（今回は、ポート番号443が使われてるって意味）

```:エラー文２
ERROR: for apache2  Cannot start service apache2: 
driver failed programming external connectivity 
on endpoint laradock_apache2_1 
Bind for 0.0.0.0:443 failed: port is already allocated
```


# ポート確認
エラー文で出たポートが本当に使われているか確認する方法を記載します。
下記のコマンドを実行すると、実際に使われているプロセスが出力されます。
出力されれば、そのポートは使用されているということなので、別ポートを使いましょう。

```:コマンド
sudo lsof -i:443
```

```:出力結果
COMMAND    PID USER   FD   TYPE DEVICE   SIZE/OFF NODE NAME
LINE       256 Ozon   25u  IPv4 XXXXXXX  0t0     TCP  XX(ESTABLISHED)
```

# ポート変更
[.env]ファイル内で、使用ポートが設定できるので、お好みのエディタでファイルを修正する。


```:.envファイル修正前
<前略>
### APACHE ################################################
APACHE_HOST_HTTP_PORT=80
APACHE_HOST_HTTPS_PORT=44３
APACHE_HOST_LOG_PATH=./logs/apache2
APACHE_SITES_PATH=./apache2/sites
APACHE_PHP_UPSTREAM_CONTAINER=php-fpm
APACHE_PHP_UPSTREAM_PORT=9000
APACHE_PHP_UPSTREAM_TIMEOUT=60
APACHE_DOCUMENT_ROOT=/var/www/
<後略>
```
↓[.env]ファイル修正
 今回は、ポート番号444に修正したが、使用していないポートだったら別の番号でも大丈夫だと思われます。

```:.envファイル修正後
<前略>
### APACHE ################################################
APACHE_HOST_HTTP_PORT=80
APACHE_HOST_HTTPS_PORT=444
APACHE_HOST_LOG_PATH=./logs/apache2
APACHE_SITES_PATH=./apache2/sites
APACHE_PHP_UPSTREAM_CONTAINER=php-fpm
APACHE_PHP_UPSTREAM_PORT=9000
APACHE_PHP_UPSTREAM_TIMEOUT=60
APACHE_DOCUMENT_ROOT=/var/www/
<後略>
```

# 動作確認
ポート番号を変更したら、下記のコマンドを実行する。

```:コマンド
docker-compose up --build -d apache2
```
上記コマンドを実行後に、下記が出力されれば、Apacheのコンテナの起動に成功しています。

```terminal:出力結果
Starting baa402ff5c7c_laradock_apache2_1 ... done
```
webブラウザ操作を自動化する(Google chrome + Selenium)

# はじめに
プライベートで友人とチャットをした後、

- チャットの管理ページにログイン
- チャットログをCSVでダウンロード
- 不要な行削除と列幅を修正してxlsxで保存

というのをやっていたので、これを自動化できないかと色々やってみました。
まだ完成していませんが、とりあえず1つ目と2つ目はできたのでメモがてら。

# 必要なもの
## ChromeDriver
ブラウザ操作を自動化するためには、ブラウザ毎に提供されているドライバーが必要になります。
今回はGoogle chromeを使ったので、ChromeDriverをダウンロードしました。

https://chromedriver.chromium.org/downloads

ダウンロード後は、任意の（適当な）フォルダに配置します。

## Selenium
GUI操作を自動化する際に、HTMLから要素を抽出してくれるUIテストツールです。
また、言語はPythonを使いました。

https://docs.seleniumhq.org/

# ソースコード

    # PythonでSeleniumのwebdriverモジュールをインポート
    from selenium import webdriver

    # ドライバーのパスを指定
    driver = webdriver.Chrome(r'C:\Users\<中略>\chromedriver.exe');

    # 対象URLにアクセス
    driver.get('http://www.xxxxxxxxxxx/yyyylogin.jsp')

    # ログイン情報を入力
    driver.find_element_by_name('id').send_keys('user')
    driver.find_element_by_name('password').send_keys('password')

    # ログイン実行(ボタン押下)
    driver.find_element_by_xpath("//input[@type='submit']").click();

    # ダウンロード実行
    driver.get('http://www.xxxxxxxxxxx/LogDownloadCSV')

# 説明（備忘録）
自分も説明できるほど理解しているとは言い難いですが、それぞれの項目について覚えた（わかった）ことをメモ書き程度に。
もう少し理解できたら改めて書くかもしれません。

### webdriver.Chrome()
使用するDriverを指定。
パスを記載する際、先頭に"r"を入れると途中のエスケープ文字などは文字列として扱ってくれるので、windowsの場合は指定するとお得だと教わった。

### driver.get()
指定したURLを開く。
リンクになっているボタンを押す際にも使う。
（今回はダウンロードページもURLだった）

### driver.find_element_by_name()
"driver.find_element_by～～"は、HTMLから操作する要素を抜き出す際に使う。
引数に何を指定するかはソースを見ながら探した。
（下記のような例の場合、"name=cof"を使う）

    <input type="hidden" name="cof" value="FORID:">

send_keys()はテキスト入力、click()はボタンをクリックする際に使用。

### driver.find_element_by_xpath()
nameやclassといった要素が指定されていない個所にテキスト入力やクリックを行いたい場合に使用するらしいけど、書き方は正直よくわからない。（宿題）
端的に言うと、上から絶対パスのように指定したりするものらしい（今回はsubmit要素が一つしかなかったから相対パスで書けた、らしい？）
【SwiftUI】丸いボタンを作成する

# 画像版

<img width="66" alt="スクリーンショット 2019-09-30 0.03.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114709/f72f4ee6-69da-13db-dc16-71d9a4c993b5.png">


```swift:
Button(action: {
    // Do something     
}) {
    Image(systemName: "pencil")
        .frame(width: 60, height: 60)
        .imageScale(.large)
        .background(Color.green)
        .foregroundColor(.white)
        .clipShape(Circle())
}
```

#Shape版
<img width="28" alt="スクリーンショット 2020-01-31 18.44.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114709/2ce54573-ff3a-1a5f-67cc-45703b04e28f.png">

```swift
Button(action: {
    // Do something   
}) {
    Circle()
        .stroke(Color.gray, lineWidth: 2)
        .frame(width: 20, height: 20)
}
```
【SwiftUI】SegmentedPickerの初期選択値を与える

Picker系はどうやら表示物に`tag(0)`のように数値のindexを与えて、それと`selection`が一致していれば選択状態になる。

```swift:
Picker("", selection: self.$selection) {
    Text("すべて").tag(0)
    Text("日別").tag(1)
    Text("週別").tag(2)
    Text("月別").tag(3)
    Text("年別").tag(4)
}
```

<img width="424" alt="スクリーンショット 2019-09-30 0.27.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114709/9fd442ee-790e-eefe-685d-db1053f2cbbb.png">

https://developer.apple.com/documentation/swiftui/picker
Java,JavaScript,C#（代入の違い)

3つの言語の定義の違いをアウトプットしてみる

#Java

```java
int a=0; 
/*整数*/
double a = 1.1234;　
/*少数点*/
char a = "文字型";
boolean a = true; 
/*true／falseのいずれかしか持たない型*/
String a = "あいうえお"; 
/*置き換えるとString a = new String("あいうえお");*/
/*基本データ型というのはJavaのまさに基本データとして元々存在している型です。
一方、クラス型というのはクラスでそのオブジェクトについて定義することで使えるようになる型です。*/
int[][] array;
int[0[]=2;
```

とまだありますがかなりきっちり定義しておかないといけない真面目な奴って印象。

#JavaScript

```JavaScript
var a=0; 
var a='あ';
/*限定なしでJavaとは違い曖昧定義*/

let a=0;
/*letを使うと変数のスコープがブロックに限定*/ 
const a=0 
/*Javaならfinal int a=0;と同義*/
/*ラストまで変更したくない場合に*/

```

曖昧な感じで好き嫌い別れいくのがモダン


#C＃

```C#
sbyte a=0;
//範囲は-128~127で、符号付き8ビット整数//
short a=0;
//範囲は-32768~32767で、符号付き16ビット整数//
int a=0;
//範囲は-2147483648~2147483647で、符号付き32ビット整数//
long a=0;
//範囲は-9223372036854770000~9223372036854775807で、符号付き64ビット整数//
byte a=0;
//範囲は0~255で、符号なしビット整数//
ushort a=0;
//範囲は0~65535で、符号なし16ビット整数//
uint a=0;
//範囲は0~4294967295で、符号なし32ビット整数//
ulong a=0;
//範囲は0~18446744073709551615で、符号なし64ビット整数//
float a=1.5;
//浮動小数点型//
//範囲は±1.5e-45~±3.4e38(おおよその範囲)で、有効桁数7桁//
double a=1.5;
//浮動小数点型//
//範囲は±5.0e-324~±1.7e308(おおよその範囲)で、有効桁数15~16桁//
bool a = true;
//true/false(ブール値)//
//真か偽の格納のみを目的とした変数の作成にはbool型が適している//
char a='X'; 
//範囲はU+0000~U+ffffで、Unicode16ビット文字//
decimal a = 199.9m;
a = 400.75M;
//範囲は±1.0×10-28~±7.9×1028で、有効桁数28~29桁の型がある。//
string a = "b " + "c";
//文字列型//
int[,]a={{1,2,3},{4,5,6}
//多次元配列1//
a[1,2]=9;
//多次元配列2//
//javaは[][]だが,で区切るだけ//

```

こちらもJavaと一緒できっちりしている印象はあったけど習得していくうちにJava+javascript的な印象も受けました。
Ruby 2.6, MySQL, Webpacker, alpine 構成の Dockerfileの作成

ブログから移動したものです。

開発環境は下記のDockerfile

```Dockerfile
FROM ruby:2.6.0-alpine3.8

ENV APP_ROOT /usr/src/app

WORKDIR $APP_ROOT

RUN apk add --no-cache alpine-sdk \
    nodejs-current \
    nodejs-npm \
    yarn \
    mysql-client \
    mysql-dev \
    python2 \
    tzdata
```


本番環境は下記のDockerfile
[Rails 5.2 + Docker, RAILS_MASTER_KEYをイメージ作成時に動的に入れる方法](https://qiita.com/hiko1129/items/c0a27ce0c46fcaeaac7b)で記載した形でbuild時に外部からRAILS_MASTER_KEYを入れられるようにしている。)

```Dockerfile
FROM ruby:2.6.0-alpine3.8

ENV RAILS_ENV=production
ENV APP_ROOT /usr/src/app
ARG RAILS_MASTER_KEY
ENV RAILS_MASTER_KEY ${RAILS_MASTER_KEY}

WORKDIR $APP_ROOT

RUN apk add --no-cache alpine-sdk \
    nodejs-current \
    nodejs-npm \
    yarn \
    mysql-client \
    mysql-dev \
    python2 \
    tzdata

COPY Gemfile $APP_ROOT
COPY Gemfile.lock $APP_ROOT

RUN bundle install --jobs=4

COPY . $APP_ROOT

RUN bin/yarn install
RUN bin/rails webpacker:compile

VOLUME $APP_ROOT/public
VOLUME $APP_ROOT/tmp
```
PHP+xmlrpcのDockerfile

## Dockerfile, compose

### ubuntu版

```dockerfile:Dockerfile
FROM php:7.3-cli
RUN apt-get update 
RUN apt-get install libxml2 libxml2-dev -y
RUN docker-php-ext-install xmlrpc
```

### alpine版

```dockerfile:Dockerfile
FROM php:7.3-alpine
RUN apk add libxml2 libxml2-dev
RUN docker-php-ext-install xmlrpc
RUN apk add vim
```

### docker-compose.yaml

```yaml
version: '2.1'

services:
  php7.3-xmlrpc:
    image: php7.3-xmlrpc
    build: .
    container_name: php7.3-xmlrpc
    tty: true
```

## 実装

### xmlrpcのサンプルコード

```php:sample.php
<?php
$a = xmlrpc_encode(['abc'=>123]);
$b = xmlrpc_decode($a);
var_dump($b);
```

```bash
> php sample.php

array(1) {
  ["abc"]=>
  int(123)
}
```
docker containerで echo $PATH

Dockerのコンテナ上で`echo`を使ってパスの確認をするには、

1.  `-c`オプションを使って
2.  コマンドをシングルクオートで


囲わないといけないみたいです。

```shell
$ docker-compose run --rm　sh -c 'echo $PATH'
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
```

参考: https://github.com/moby/moby/issues/13763
　　

`-c`オプションを使わなかったり、ダブルクオートで囲んだりすると、ホストマシンのパスが出力されます。

```shell
$ docker-compose run --rm echo $PATH
```

```shell
$ docker-compose run --rm sh -c "echo $PATH"
```

なんでこうなるのかはよくわかりません。
高機能画像エディターライブラリ「TOAST UI Image Editor」を導入してお手軽画像編集！

#TOAST UI Image Editorとは
HTML5 Canvasを使用した、高機能でお手軽な画像エディターをユーザーに提供することができるライブラリです。
スタンプや、文字入れをはじめ、白抜きやピクセレート、反転などの機能があります。

https://ui.toast.com/tui-image-editor/
https://github.com/nhn/tui.image-editor

#導入
```
npm: npm install --save tui-image-editor
yarn: yarn add tui-image-editor
```

#使い方
`const ImageEditor = require('tui-image-editor')`
で、モジュールを読み込み
`new ImageEditor(document.querySelector('#tui-image-editor'), option)`
とすることで
`id="tui-image-editor"`であるノードへ、エディター画面が子要素として挿入されます。

ポイントは、アイコン・CSSを別途インポートする必要があることと、
`includeUI.loadImage.path`に編集対象画像のURLを与える必要があることです。
FileオブジェクトからURLを取得する方法は、
[【Blob・File・Base64・データURL・FileReader】 それぞれの特徴とブラウザへの表示について](https://qiita.com/allein-s/items/13d0e2fc0896df83b234)
を参考にしてください！

```js
// CSSのインポート
import 'tui-image-editor/dist/tui-image-editor.css';
import 'tui-color-picker/dist/tui-color-picker.css';
// アイコンデータのインポート
import icon_a from 'tui-image-editor/dist/svg/icon-a.svg';
import icon_b from 'tui-image-editor/dist/svg/icon-b.svg';
import icon_c from 'tui-image-editor/dist/svg/icon-c.svg';
import icon_d from 'tui-image-editor/dist/svg/icon-d.svg';

const ImageEditor = require('tui-image-editor')
const locale_ja = {
  'Load': 'ファイル選択',
  'Download': 'ダウンロード'
  // etc...
}
const theme = {
  // メインアイコン
  'menu.normalIcon.path': icon_d,
  'menu.activeIcon.path': icon_b,
  'menu.disabledIcon.path': icon_a,
  'menu.hoverIcon.path': icon_c,
  // サブメニューアイコン
  'submenu.normalIcon.path': icon_d,
  'submenu.activeIcon.path': icon_c,
}
const imageEditor = new ImageEditor(document.querySelector('#tui-image-editor'), {
  includeUI: {
    loadImage: {
      path: base64String, //編集対象画像のURL
      name: 'Sample'
    },
    locale: locale_ja,
    theme: theme,
    initMenu: 'filter',
    menuBarPosition: 'bottom'
  },
  cssMaxWidth: 700,
  cssMaxHeight: 500,
  selectionStyle: {
    cornerSize: 20,
    rotatingPointOffset: 70
  }
})
```

#注意
`includeUI.loadImage.path`にサーバー上の画像パスを与えると、CORSポリシーにより弾かれ、正常に動作しません。
工夫して、サーバー上のファイルを直接取得させずにデータを渡す必要があります（ご存知でしたらお教えください・・・）

また、デフォルトのエディタには編集後画像をアップロードするボタンが存在せず、ダウンロードボタンしかありません。
replaceChildなどを使って、`tui-image-editor-download-btn`クラスのノードを自作のダウンロードボタンと置換するなどする必要があります。

また、編集後画像は上記例では、`imageEditor.toDataURL()`で、Base64文字列形式で取得することができます。
Javaからバッチファイルを実行する

__注意: この記事は次の環境で確認を行いました。__

- Microsoft Windows 10 Pro Edition
- OpenJDK 11
- Eclipse 2019-09 (Pleiades All in One)

---

`C:\sample`というフォルダに`helloworld.bat`が設置されているとします。内容は次の通りです。

```cmd:helloworld.bat
@echo off
echo Hello World 01
echo Hello World 02
echo Hello World 03
```

この`helloworld.bat`をJavaから実行するサンプルが以下になります。

```java
// ProcessBuilderの生成
// helloworld.batのstdinとstderrを親プロセスのstdinとstderrにつなぐ。
var processBuilder = new ProcessBuilder("C:\\sample\\helloworld.bat");
processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);

// Processの生成
// helloworld.batを子プロセスとして実行し、プロセス実行が終わるまで親スレッドを停止する。
var process = processBuilder.start();
process.waitFor();
```

これを実行すると、次のような文字列がJavaの標準出力に出力されているはずです。

```
Hello World 01
Hello World 02
Hello World 03
```

__個人的に「はまった」ポイントとしては、バッチファイルの拡張子。実行したいファイルの拡張子はbatまたはcmdである必要があります。__ 

いわゆるシェルスクリプトの世界では、スクリプトが記述されたファイルの拡張子に指定はありません。慣習的に`sh`とすることが多いと思いますが、実行権限さえ付与されていれば、`txt`でも`html`でも実行可能ですし、拡張子がなくても問題ありません。

一方、Windowsの世界では、そのファイルがバッチファイルかどうかを判定するにあたって、拡張子を利用しています。つまり拡張子がbatもしくはcmdの場合、そのファイルを実行可能なバッチファイルとみなすわけです。

自分がはまっていたのは「拡張子batでもcmdでもないファイルを`ProcessBuider`や`Process`で実行しようとして、うまくいかない」というところでした。わたしはいわゆるオープン系のアプリケーション開発者です。その世界(つまりLinuxやUnix)では実行権限が正しく付与されていれば、拡張子のいかんを問わず実行できてしまうわけで、「特定の拡張子を指定する必要がある」という発想になかなかいたりませんでした(´・ω・｀)
Goのプロジェクトレイアウト

#Goのプロジェクトレイアウト

ここのGithubの和訳です。私的な備忘録なのでだいぶ意訳です。
Goプロジェクトのディレクトリをどう切ろうかと考える際の参考程度に。
https://github.com/golang-standards/project-layout

|ディレクトリ|用途|
|:---|:---|
|/cmd|プロジェクトのメインのアプリを配置する. `/cmd` 配下のディレクトリ名は, 最終的な実行バイナリ名と同じにすると良い(e.g.,`/cmd/myapp`). そのため `/cmd` 以下のディレクトリは基本的に `package main` になる. ここへは沢山のコードを配置すべきではない. もし他のコードやプロジェクトから import される類のコードである場合は `/pkg` ディレクトリに配置するべきだし, もしコードが再利用不可あるいは再利用されたくない場合は `/internal` に配置すべき. 一般的に `/cmd` には `/internal` や `/pkg` ディレクトリから import して実行する小さい `main` 関数が置かれる.|
|/internal|プライベートなアプリやライブラリ, 他のアプリやライブラリから import されたくないコード. アプリであれば `/internal/app` ディレクトリに配置する(e.g.,`/internal/app/myapp`). また, それらプライベートなアプリから利用されるコードであれば `/internal/pkg` ディレクトリに配置する(e.g., `/internal/pkg/myprivlib`).|
|/pkg|外部のアプリから利用OKなライブラリコード.(e.g., `/pkg/mypubliclib`). 他のプロジェクトからこれらのライブラリが import されることを想定して, 本当にここにコードを配置すべきかはよく検討すべき. `/pkg` は一般的な構成パターンだが, ごく一部のコミュニティでは利用を推奨していない.|
|/vendor|アプリの依存関係(手動もしくは `dep` など, 任意の依存関係管理ツールで管理される). もしライブラリ開発のプロジェクトであれば, アプリの依存関係をコミットしてはいけない.|
|/api|OpenAPI/Swagger仕様, JSONスキーマ, プロトコル定義などを配置.|
|/web|WEBアプリの資材を配置する. 静的コンテンツやサーバサイドテンプレート、Single Page Applicationsなど.|
|/configs|アプリ設定ファイルのテンプレートやデフォルト設定を配置する.|
|/init|System init(systemd, upstart, sysv)やプロセスマネージャ/スーパーバイザ(runit, supervisord)の設定を配置.|
|/scripts|ビルドやインストール, 分析などの用途のスクリプトを配置. このディレクトリで root 階層の `Makefile` を小さく保つ.|
|/build|パッケージングやCIのために使用. AMIやDocker, OS(deb,rpm,pkg)のパッケージ設定やスクリプトを `/build/package` に配置する. CI(travis, circle, drone)設定は `/build/ci` に配置する.|
|/deployments |IaaS, PaaS, コンテナオーケストレーションなどのデプロイ設定やテンプレート(docker-compose, kubernetes/helm, mesos, terraform, bosh).|
|/test|追加のサードパーティテストアプリやテストデータ. 中の構成は自由で, `/test/data` や `/test/testdata` にデータを置いたり, `/test/e2e` というディレクトリを切ってテスト用のコードを配置したり.|
|/docs|デザインやユーザドキュメント(godocで生成したドキュメント以外に).|
|/tools|このプロジェクトのサポートツール. これらのツールは `/pkg` や `/internal` からコードを import 可能.|
|/examples|アプリやライブラリの使用例.|
|/third_party|外部のヘルパーツール, Forkしたコードやサードパーティユーティリティ(e.g., Swagger UI).|
|/githooks|Git hooks.|
|/assets|その他, リポジトリに関連する資材(images, logos, etc).|
|/website|Github pagesを利用していない場合はここにプロジェクトのWebサイト用データを配置.|
|/src|`/src` は使うべきではない.|
軽量かつ高速なロガーzapを導入する

# 目的

Uberの提供するOSSのGo言語用Loggerのzapの導入と利用方法を簡単に説明する。
備忘録的に概要を記述しているだけなので、
詳細は、公式リポジトリ、[user-go/zap](https://github.com/uber-go/zap)を参照すること

# インストール

```console:zapの導入
$ go get -u go.uber.org/zap
```

# ロガーの記述方法

```golang:サンプルコード
import (
	"go.uber.org/zap"
)
// 〜〜〜省略〜〜〜
    logger, _ := zap.NewProduction()
    defer logger.Sync()

    logger.Info("Response received.",
	zap.Time("timestamp", response.Timestamp),
	zap.String("url", response.URL),
	zap.String("method", response.Method),
	zap.Int("status_code", response.StatusCode),
	zap.Int("response_time", response.ResponseTime))
```

`logger, _ := zap.NewProduction()`でロガーを生成。
`defer logger.Sync()`で関数を抜ける際にログをフラッシュしてバッファに残っている
ログレコードを書き込む模様。

`logger.Info`でInfoレベルでログを書き込み、他にも`Warn`など一般的なログレベルを指定して出力できるようになっている。
関数の引数として、ログメッセージ(msg)を指定する。
`zap.型`で型を指定してログレコードに付与する各種情報を出力する。

```json:ログの出力例
{"level":"info","ts":1569769104.776992,"caller":"client/http_client.go:38","msg":"Response received.","timestamp":1569769104.5155523,"url":"http://web.ryoma0923.work/","method":"GET","status_code":200,"response_time":260}
```

ログはLDJSON(Line Delimited JSON)で出力される。
Rails 5.2 + Docker, RAILS_MASTER_KEYをイメージ作成時に動的に入れる方法

ブログから移動したものです。

下記のような形でDockerfileを作成する。

```Dockerfile
FROM ruby:2.6.0-alpine3.8

ENV RAILS_ENV=production
ENV APP_ROOT /usr/src/app
ARG RAILS_MASTER_KEY
ENV RAILS_MASTER_KEY ${RAILS_MASTER_KEY}

WORKDIR $APP_ROOT

RUN apk add --no-cache alpine-sdk \
    nodejs-current \
    nodejs-npm \
    yarn \
    mysql-client \
    mysql-dev \
    python2 \
    tzdata

COPY Gemfile $APP_ROOT
COPY Gemfile.lock $APP_ROOT

RUN bundle install --jobs=4

COPY . $APP_ROOT

RUN bin/yarn install
RUN bin/rails webpacker:compile

VOLUME $APP_ROOT/public
VOLUME $APP_ROOT/tmp
```

下記の部分がポイントとなっている。

```Dockerfile
ARG RAILS_MASTER_KEY
ENV RAILS_MASTER_KEY ${RAILS_MASTER_KEY}
```

下記のような形でCI上で環境変数をARGとして渡してやれば（CI側の環境変数にRAILS_MASTER_KEYが設定されている前提）、上記のARGにRAILS_MASTER_KEYが設定され、ENVのRAILS_MASTER_KEYにARGのRAILS_MASTER_KEYを設定することができる。これにより、イメージ作成時にRAILS_MASTER_KEYを設定した状態でビルドすることができる。

```
docker build --build-arg RAILS_MASTER_KEY=${RAILS_MASTER_KEY} -t app . -f Dockerfile.production
```

参考:
* https://qiita.com/NaokiIshimura/items/2a179f2ab910992c4d39
* https://qiita.com/nacika_ins/items/cf8ceb20711bd077f770
【Rails】kaminariで作ったページネーションの1ページあたりの表示件数を変更する方法

kaminariで作ったページネーションは、デフォルトだと25件まで表示する設定になっています。

1ページあたりの表示件数を変更するには、config/initializers/kaminari_config.rbを編集すればOK。


今回は1ページあたり10件の表示に変更することにしましょう。

config/initializers/kaminari_config.rbを開くと、コメントアウトされている項目がたくさんあると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266250/9dda3461-8d23-f101-d3b1-326388da3ab3.png)


表示件数を変える場合は、1番上の# config.default_per_page = 25の#を取って、以下のように変更してください。


```config/initializers/kaminari_config.rb
config.default_per_page = 10
```


変更したら、rails sでサーバーを再起動してください。

表示件数が10件になっているはずです。

 

ちなみに、他の項目は以下のような意味です。


【default_per_page】

- 1ページあたりの表示件数


【max_per_page】

- 1ページあたりの表示件数
- デフォルトはnil = 無制限



【window】

- 左右何ページ分のリンクを表示するかの設定
- 3に設定して現在10ページ目にいる場合、7・8・9ページと11・12・13ページが表示される


【outer_window】

- 最初のページ・最終ページからそれぞれ何ページ分のリンクを表示するかの設定
- ３と設定して全20ページの場合、1・2・3ページおよび18・19・20ページが表示される


【left】

- 先頭ページから何ページ分のリンクを表示するかの設定


【right】

- 最終ページから何ページ分のリンクを表示するかの設定



【page_method_name】

- ページ番号を指定するスコープの名前



【param_name】

- ページ番号を渡すのに使うリクエストパラメータの名前



以上です。


以上です。
GitHub Actions v2 の LINE Notify アクションを作ってみた

# はじめに
[GitHub Actions を用いて issue が更新されたら LINE に通知する方法 - Qiita](https://qiita.com/osakiy/items/dd77a31bf25d27dd0679)
こちらの記事を見て LINE Notify を知り、習作にちょうど良さそうだったのでアクションを自作してみました。

# 完成品
ソースコードはこちら。
[snow-actions/line-notify: LINE Notify for GitHub Actions](https://github.com/snow-actions/line-notify)

Marketplace にも公開していますので良かったらご利用ください。
[LINE Notify · Actions · GitHub Marketplace](https://github.com/marketplace/actions/line-notify)

## 使い方
```yml:.github/workflows/example.yml
name: LINE Notify example
on:
  issues:
    types: opened
jobs:
  notify:
    runs-on: ubuntu-latest
    steps:
      - uses: snow-actions/line-notify@v1.0.0
        with:
          access_token: ${{ secrets.LINE_ACCESS_TOKEN }}
          message: ${{ github.event.issue.title }}
```
Settings > Secrets に [LINE Notify で取得した](https://qiita.com/iitenkida7/items/576a8226ba6584864d95)アクセストークンを `LINE_ACCESS_TOKEN` を設定してください。

# 作り方
[アクションのビルド - GitHub ヘルプ](https://help.github.com/ja/articles/building-actions) に説明があります。
今回は JavaScript で作ります。

## アクションの作成
[JavaScript アクションを作成する - GitHub ヘルプ](https://help.github.com/ja/articles/creating-a-javascript-action) に従って作ります。
リポジトリを作ってクローンして `npm init -y` しておきます。
Node.js 12.x が必要です。
`action.yml`, `index.js` を作成。

[JavaScript テンプレート](https://github.com/actions/javascript-action)も公開されているのでそちらから作っても良いです。
`Use this template` ボタンを押すとリポジトリが作られます。
他にも [TypeScript](https://github.com/actions/typescript-action) や [Docker](https://github.com/actions/hello-world-docker-action) のテンプレートがあります。

## node modules
今回は HTTP リクエストで API を叩きたいので `@action/core` に加えて `request` をインストールします。

```shell
npm install @action/core
npm install request
```

`node_modules` ディレクトリもリポジトリに含めておく必要があるので .gitignore から外しておきます。


## Marketplace へ公開
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455401/9b37e1e5-7e94-5aea-447b-7f7c7065abb0.png)
`Draft a release` ボタンを押すと新規リリース作成画面へ飛ばされます。
公開するには規約への同意とアカウントが2ファクタ認証している必要があります。
指示されるのでそれに従えば良いです。

存在しているタグを選ぶか新規にタグを作成します。
リリースのタイトルや説明を入力します。

[branding](https://help.github.com/ja/articles/metadata-syntax-for-github-actions#branding) の設定をしておくことを推奨します。
README.md がそのまま Marketplace の説明に使われます。

審査もなくリリースを作成したらすぐに Marketplace へ公開されました。

# おわりに
アクションは簡単に自作して公開まで出来るので欲しいアクションがあったら作ってみるのもいいかもしれません。
[LINE Notify API Document](https://notify-bot.line.me/doc/ja/) を見ると `message` 以外にも情報を付加できそうなので Pull Request も歓迎です。
ChromeでAWS Cloud9に接続できないときの対処方法

# はじめに

Ruby On Railsの学習のため、AWS Cloud9を使っています。
その学習中にCloud9に接続できなくなってしまいました。

その際に試した対処法をまとめます。

# 環境

- OS：MacOS Mojave バージョン 10.14.6
- ブラウザ：Chrome バージョン76

# 事象

Cloud9を開いていたタブを閉じて、数分後にまたCloud9にアクセスしたときに発生しました。
表示されたメッセージは以下になります。

```
This is taking longer than expected. If you think there might be an issue, contact AWS Support.
It might be caused by VPC configuration issues. Please check documentation.
```

※ スクショはとっていませんでした…

# やったこと

- Chromeのキャッシュを削除（変化なし）
- Chromeのバージョンアップ確認（そもそもバージョンアップなし）
- EC2の再起動（変化なし）
- 使用するブラウザを変更

# 使用するブラウザを変更

Chromeではなく、Safariを使用したら正常にアクセス、使用することができました。
Firefoxでも正常にアクセスできました。

ちなみに、後日Chromeで接続しても、正常にアクセスすることができました。
Elastic Beanstalk 利用時に httpでのアクセスを https にリダイレクトする方法

Elastic Beanstalk側でコンソール使用して設定できる項目がなさそうだったので、nginx側で下記のような設定を行った。

```nginx
server {
  listen 8080;
                      
  location / {             
    return 301 https://$host$request_uri;          
  }
}  
```

Elastic Beanstalkのコンソール側では、下記のように80番ポートがnginxの8080番ポートに行くように設定してやれば良い模様。
<img width="512" alt="aws-console-elastic-beanstalk.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117867/05175a01-e67c-2233-0689-471a9115a6c8.png">
【Rails】kaminariのページネーションを日本語化する方法


###kaminariを日本語化する方法
kaminariを日本語化する方法はたったの２ステップです。


###ステップ１：ステップ1：config/application.rbを編集
config/application.rbを開いて、以下のコードを書いてください。


```config/application.rb
config.i18n.default_locale = :ja
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266250/ee719e00-307d-fccc-5b08-28fc1a7f9c7f.png)




###ステップ2：config/lacales/ja.ymlを作成
次にconfig/lacales/ja.ymlといファイルを作りましょう。

ファイルを作ったら、以下のように記述します。


```config/lacales/ja.yml
ja:
  views:
      pagination:
        first: '最初'
        last: '最後'
        previous: '前'
        next: '次'
        truncate: '...'

```


記述したら、rails sでサーバーを再起動してください。

以下のように、日本語になっていたら成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266250/8b273829-c72c-7bb1-5f14-a3abb3c60e16.png)