OmniAICreator/Qiita-1.07M huggingface dataset:
Javaでコンソールでのキー入力を１文字ずつハンドルしたいときにJLineを使う

# 概要

このエントリでは、Javaでコンソール入出力で動作するプログラムを書くときに、1文字ずつ入力をハンドリングしたい場合の方法について扱います。

# 背景

Javaでは、標準入出力を扱うStreamがありますが、1文字ずつ入力を「読み込む」ことはできますが、キー入力のイベントを直接ハンドルすることはできません。

このため、例えば「電卓」プログラムを作るときに「+」を押したらすぐに何かの処理を行う、といったことは標準のライブラリだけでは実現できません。

# JLineを使う

## JLineとは

[JLine3](https://github.com/jline/jline3)は、CUIアプリケーションを開発するときに使えるライブラリです。いくつかの有名どころのOSSでも利用されています。

JLine3は、Windows環境などでのプラットフォーム独自のターミナルの制御にも対応します。ここではプラットフォームネイティブのライブラリが必要となるため、[JNA](https://github.com/java-native-access/jna)もしくはJANSIを使用する方式が提供されています。このエントリでは、JNAを使用しています。

## コードサンプル

下記のコードを使うと、1文字ずつターミナルから入力し、即時に標準出力に表示しています。

```Java
package com.hrkt.commandlinecalculator;

import lombok.extern.slf4j.Slf4j;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
@Slf4j
public class CommandlineInterface {
    public void run(String... args) {
        log.info("hello");

        try(Terminal terminal = TerminalBuilder.terminal()) {
            int ch = 0;
            while ((ch = terminal.reader().read()) != 0x09){
                // TAB(0x09)で抜ける
                char c = (char)ch;
                System.out.println(String.format("%d, %c", ch, ch));
            }
        } catch(IOException e) {
            log.error(e.getMessage(), e);
        }
    }
}

```

下図のような動きとなります。

![char-by-char.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65002/97f7759a-8fde-cfec-723e-b91c638ee5fa.png)

# おわりに

このエントリでは、Javaでコンソールからのキー入力をハンドルする方法について紹介しました。
JLine2を利用した記事も見かけますが、開発が終わっているものであり、JLine3を使うことが推奨されています。

## コードのサンプル

動作するサンプルは、下記に置いてあります。

https://github.com/hrkt/commandline-calculator/releases/tag/0.0.2
current_page?で場合分け ❏Rails❏

newとeditで同一の部分テンプレートを利用しているが、それぞれちょっとだけ変えたい時

`current_page?(URL)`で指定したページにいるか判定してくれます。

```ruby:_form.html.haml
- if current_page?(new_tweet_path)
  %p 新規作成
- elsif current_page?("/tweets/#{tweet.id}/edit")
  %p 編集

= form_with model: tweet, local: true do |form|
...
```

```ruby:new.html.haml
= render "form", tweet: @tweet
```

```ruby:edit.html.haml
= render "form", tweet: @tweet
```

newページでは`新規作成`が、
editページでは`編集`が表示されるようになりました。

<br>
`(URL)`の部分は以下が可能みたいです。
エラーが出たらいくつか試してみてください。

###・URL
`current_page?(http://hoge.com/hoge)`

###・パス
`current_page?(/tweets/new)`

###・prefix
`current_page?(new_tweet_path)`

###・アクション指定
`current_page?(action: "new")`

###・コントローラー&アクション指定
`current_page?(controller: "tweets", action: "new")`

<br>
参考
>http://shonoooo.hatenablog.com/entry/2017/10/31/232302

<br>
ではまた！
Cloud Runは便利！GCPの月額費用を4万円以上節約した話

[g4](https://www.g-g-g-g.games)というサービスを運営しているのですが、ここ数ヶ月間でGCPの月額費用を4万円以上節約したので、経緯を書いておきます。

7月の費用 ¥53,818
9月の費用 ¥18,801 詳しくはこちらの[レポート](https://note.com/shwld/n/n3c9a16a7ba4c)
11月の費用 ¥7,333 詳しくはこちらの[レポート](https://note.com/shwld/n/n29cfd510f515)

5月はこんな感じでした。
![スクリーンショット 2019-12-03 6.27.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51402/3bd4c542-9079-e7c5-fc19-d96a166240bd.png)

完全にやらかしてますね！！敗因は

- 無料期間なのもあり利用料金気にせずに使っていた
- そして無料期間の終わりを忘れていた
- アラートを設定していなかった

です。

まずは、

## 7月の費用から約3万円節約するために、GAEのオートスケール周りの設定を見直しました

7月の状態は

- 本番: (GAE flexible/custom cpu x 2) x 2台
- ステージング: (GAE flexible/custom cpu x 2) x 2台

（DBに関しては本番とステージングで同じインスタンスを使っています）

でした。これは、app.yamlの設定をほぼ何もしなかったためなので、設定を変更して単純にcpuを1つにしたうえでインスタンス増えないようにしました。

``` app.yaml
resources:
  cpu: 1

manual_scaling:
  instances: 1
```

これだけ！

結果、約2万円くらいにまで下がりました。

### それでも2万円近い月額。ちょっとつらい
そもそもステージングに7千円近くかけたくない...タダにして

ステージングは結構稼働してないことが多く、使ってないときは料金かからないと嬉しい。
使うときだけ手動で立ち上げるとかも考えましたが、やっぱり自動でやりたいのでベータ版であるAppEngineのStandard環境(ruby)にすることを決意しました。

あとStandard環境には無料枠があるのでそれによる節約も期待できます。

## GAE (Flexible環境/Docker) をStandard/ruby環境に移行

g4のバックエンドはシンプルなRuby on RailsのGraphQL APIサーバーなのですが、[mecab](https://taku910.github.io/mecab/)を使っている箇所があります。
mecabはOSへのインストールが必要で、Dockerを使えるカスタムランタイムでインストールして使っている状態でした。
Standardのrubyイメージを使うにはこいつが邪魔だった。

なのでこいつを外部API化して別に切り出してしまうことを検討しました。

移行先はこいつ[CGPのCloud Run](https://cloud.google.com/run/?hl=ja)です。すでにg4では利用実績が合ったので、イニシャルコストが安いこともわかっていたこいつを使うことにしました。

``` app.rb
# encoding: utf-8

require 'sinatra'
require 'natto'

get '/hogehoge' do
  # 処理してjson返す
end

not_found do
  content_type :txt
  "404 page not found."
end

```

Railsからの切り出しなので、こんな感じでsinatraにしたらサクッと20行くらいのコード+Dockerfileとか設定ファイルだけで済んでしまいました。めっちゃ簡単じゃん。

スタンダード環境化であとやったことは

- mecabを使っている箇所でAPIリクエストするようにコードを変更して、依存を消す
- app.yamlの設定をrubyのstandardに変更して、オートスケールなど設定

くらいです。

app.yamlについてはとりあえずこんな感じにしてみました。

``` app.yaml
env: standard
runtime: ruby25

automatic_scaling:
  max_instances: 5
  max_concurrent_requests: 40
  target_cpu_utilization: 0.6
resources:
  cpu: 1
  memory_gb: 0.5
  disk_size_gb: 10
```

1ヶ月ちょっとやってみて3台くらいインスタンスが立ち上がっていることも割と多い印象。
立ち上がりに若干時間かかる気がするので、最低インスタンス数とか設定は見直していきたい。

結果、サーバー費用は200円くらいに収まり、現在はほぼデータベースの約7千円のみに落ち着きました。

![スクリーンショット 2019-12-02 6.41.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51402/465019fa-b621-fa24-2aee-86a0d48a6a70.png)

データベースは今後データも多くなるし、ある程度コストかかってもしかたないかなって思っているのでこれにて一旦節約プロジェクトは完了です！

## 忘れずに金額アラートを設定

GCPの予算とアラートのページからアラートを設定できるので設定しておきましょう。
とりあえず1万円で設定して、5千円を超えたら通知が来るようにしました。

## まとめ

- 無料期間中でもしっかり料金計算して必要な分だけ使うようにしましょう
- CloudRunでマイクロサービス化するといろんな依存を外出しできるので活用しましょう
- アラートは設定しておきましょう

という自分への戒め

<a href="https://www.g-g-g-g.games"><img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51402/ccc31c7a-207e-77f9-363b-75e39896b7df.png"></a>

こんな感じでg4というサービスを開発しています。
ポモドーロ・テクニックを用いて現実で頑張った分だけ成長できるゲームのようなサービスなので、興味があったら触ってみてくださいね。
Visual studio codeの導入とpythonのインストール

備忘録としてwindowsでのpythonを使ったslackbotの作成を目的とした流れを書いていきます。
まずはVisual studio codeの導入とpythonのインストールを。

vscodeの公式に行きインストールします。
![visualcode画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542570/0198874c-4363-ecc3-5a1b-02f8873d7f5f.png)
基本的にはデフォルトでインストール。デスクトップにアイコン置く設定に変えたくらい。


早速vscodeを起動すると英語なので日本語化します。
![日本語化.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542570/39f7e999-02a2-c58e-a3b9-060861425e1e.png)
左下の赤く囲んだ部分をクリックするとMarketplaceの検索窓が開くのでJapaneseと検索、Japanese Language Pack for Visual Studio Codeをインストールして再起動すると日本語化が完了します。

![日本語画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542570/907d590c-ee7f-40ff-27c7-8f965e3cbde8.png)
日本語化された画面。



次にpythonのインストール。
公式に飛んでpythonをインストールします。3.8にまだ対応してないところもあったので3.7.5にします。
![pythonインストール.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542570/8c1ec451-8bd9-98f9-c776-b374bf31bb9b.png)

windows 64bit版の人はWindows x86-64 web-based installer、32bit版の人はWindows x64 web-based installerをダウンロード。
![python375.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542570/6038bf1f-07fe-296c-229d-51e8c7f87d69.png)

環境変数設定のためPATHにチェック入れてインストールします。
![python install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542570/ef1d1b95-52e8-a5bd-5fab-cd9e9ccbc13a.png)

終わったらインストール出来ているかどうかチェック。コマンドプロンプト開いてpythonコマンドを実行。

```
C:\Users\tokoroten>python
Python 3.7.5 (tags/v3.7.5:5c02a39a0b, Oct 15 2019, 00:11:34) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
```
version3.7.5がインストールされている事が確認できました。

次にvsCodeでのpython実行の準備をします。
日本語化の時と同様にMarketplace開いてpythonと入力してインストール。
![python.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542570/0b68325b-9948-439a-7249-43742adfd090.png)

vsCodeでpythonの準備が出来たので早速試します。
Cドライブ直下にworkspaceフォルダ作って適当に"hello world"を表示するpythonファイル作ります。
(今回はpython_study/helloにhello.py作りました)
vsCode下部のターミナルから

```
python Hello.py
```
入力して実行。
![hello world.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542570/59200d56-4046-f8d8-9c97-3d7c3dca0208.png)
無事Hello worldが表示されたのでpythonが実行されていることが確認できました。



次の記事はslack側の設定していきます。
【XCUITest】UITextFieldの値の削除と入力(iPadOS13/Xcode11対応)

# はじめに
iOSアプリ開発でXCUITestを実装しており、その際に、「UITextFieldの値を削除し、そこに新しい値を入力する」という処理が必要になりました。
stack overflowのこちらの記事( [UI Test deleting text in text field](https://stackoverflow.com/questions/32821880/ui-test-deleting-text-in-text-field) )を参考に、XCUIElementのExtensionでclearAndEnterTextを作成することでiOS12(Xcode10)では期待通り動いていたのですが、iPadOS13(Xcode11)に上げてからUITextFieldの値が削除されなくなってしまったので、その対応を行いました。その対応方法を記載します。

# 前提
- Xcode Version 11.2 
- iPadOS13


# iOS12(Xcode10)では期待通りに動いていたclearAndEnterText
[UI Test deleting text in text field](https://stackoverflow.com/questions/32821880/ui-test-deleting-text-in-text-field) からclearAndEnterTextのコードを抜粋します。
UITextFieldをタップすることで、カーソル位置が入力されている値の最後尾に来るので、そこからdeleteStringを文字数分入力することで値を削除しています。

```swift
 
extension XCUIElement {
    /**
     Removes any current text in the field before typing in the new value
     - Parameter text: the text to enter into the field
     */
    func clearAndEnterText(text: String) {
        guard let stringValue = self.value as? String else {
            XCTFail("Tried to clear and enter text into a non string value")
            return
        }

        self.tap()

        let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: stringValue.count)

        self.typeText(deleteString)
        self.typeText(text)
    }
}
```

# iPadOS13(Xcode11)でUITextFieldの値が削除されなくなってしまった原因
Xcode11から、UITextFieldをタップするとカーソル位置が入力されている値の先頭に移動するようになり、deleteStringを入力しても1文字も消えなくなっていました。

# iPadOS13(Xcode11)対応のために修正したclearAndEnterText
UITextFieldをダブルタップすることで、テキストを全選択し、deleteStringを1度入力することで値を削除するようにしてみました。

```swift
 
extension XCUIElement {
    /**
     Removes any current text in the field before typing in the new value
     - Parameter text: the text to enter into the field
     */
    func clearAndEnterText(text: String) {
        // ダブルタップでテキストを全選択
        self.doubleTap()

        // deleteは1回のみ 
        let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: 1)

        self.typeText(deleteString)
        self.typeText(text)
    }
}
```

# おわりに
他にも対応方法はあると思いますが、私のケースではこの方法で期待通りにテストできるようになったので、一旦これで良しとしています。

# 参考
- [UI Test deleting text in text field](https://stackoverflow.com/questions/32821880/ui-test-deleting-text-in-text-field)
flutter hello world

# はじめに
今回はWindowsでflutterを動かしてみようと思います。本当はMacでやりたいけど、持ってないのでしょうがない、、、

# 環境構築
スマホに表示できるところまでやりたいと思います。
基本的には[Flutter公式ドキュメント](https://flutter.dev/docs/get-started/install/windows)を見れば大体書いてあるので、確認しながらやればいいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187255/59712f2e-8514-0bc6-5257-0fe4c39eb2b9.png)

Get startedの3番まで、正しく設定すれば動きます。

## 環境変数に追加

flutterの環境設定をするために、まずは公式サイトに移動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187255/0a4d1115-14f1-28ab-0792-856484567481.png)

画像の青いボタンをクリックしてflutterアプリケーションをダウンロードします。

ダウンロードしたフォルダを解凍後、フォルダ内の [/flutter/bin] を環境変数に追加します。

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187255/237525b7-3d67-89df-d129-c218ba2f96a5.png)

## flutter doctor

環境変数の追加が終わったら次は以下のコマンドを実行します。

```
PS C:\> flutter doctor
```

全て解決しないと動かないようなので、上から解決していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187255/8e1380da-4be1-150b-e64e-6f2c07eb7ce0.png)

一番上は下のコマンドを実行すれば終わりです。

```
PS C:\> flutter doctor --android-licenses
```

2番目はAndroid Studioをインストールするところから始めます。

インストールの手順は公式サイトを見るか、別のサイトの手順を参考に。

インストールが終わったら、Google USB Driverとfultter pluginのインストールを行います。

[Google USB Driverはこちら](https://developer.android.com/studio/run/win-usb)

fultter pluginは メニュー File > Settings > Plugins の順で移動して、以下の画像のように検索すればインストールできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187255/becacc68-af63-8660-ccfb-290b24638837.png)

これでAndroid Studioは終わりです。

エディタのセットアップを行います。使えるのは Android Studio, IntelliJ, Visual Studio Code の３つ。

VSCodeでやりたいので、VSCodeに拡張機能のflutterを導入する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187255/4884c4f2-6711-4d87-d801-9a0f66628bcf.png)

後はデバイスをUSBデバッグで接続すれば、以下の画像のようになるはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187255/ef611ff0-e68f-bdee-4ca8-7260ea7221cf.png)

## hello world
新しいflutterプロジェクトを作成します。「Ctrl + Shift + P」でコマンドパレットを開き、[flutter:New Project]を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187255/a0b0287c-7b32-c73e-4132-6ea468cda441.png)

名前を入力を要求されるので、今回は[helloworld]を入力してエンターを押します。保存先を聞かれるので好きな場所に保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187255/50050592-fd0c-cfd8-8f5c-6b92d150c19d.png)

後はプロジェクトが作成されるので、待ちます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187255/420164e6-56a2-622a-ba25-88ad56f4297f.png)

こんな感じのプロジェクトが作成されたら、[main.dart]を開きます。開いたら[F5]を押してもらえれば実行できます。以下の画面をデバイスで実行できれば成功です。

![Screenshot_20191204-065212192.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187255/7ed6b43b-755d-3eec-bc16-76cc887aee11.jpeg)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187255/2b2a438f-18e0-d62d-2463-4b7252d2cf02.png)

ステータスバーに接続中のデバイスが表示されない場合は、接続がおかしいので確認してください。

## 最後に
簡単に書きましたが、本当にこれで動くので自分で1から作るより簡単にアプリが作れるのは良いと思いました。
結構簡単にスマホアプリが作成できそうなので、何かアプリでも作れればまた書こうと思います。


ではでは
【Mac版】Visual Studio CodeでPlantUMLを使う時のプレビュー表示とエクスポートのショートカットキーメモ

# はじめに
最近、Visual Studio CodeでPlant UMLを使ってクラス図やシーケンス図などを作成するようになったのですが、ちょっと間が空くと、すぐに**プレビュー表示**や**エクスポート(ファイル出力)**のためのショートカットキーを忘れてしまうのでメモを残しておきます。

# 前提
- Mac PC
- Visual Studio Code
- Plant UMLインストール済み


# ショートカットキー(とちょっとした操作メモ)
- **プレビュー表示**：`option + D`
![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145054/9ce49aa5-3ed3-08ba-0192-037e1ccb181b.png)


- **エクスポート**：`command + shift + P` -> （パレットが開く） -> PlantUML: ファイル内のダイアグラムをエクスポート(Export Current File Diagrams) -> ファイル形式を選択してエクスポート
![図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145054/e7dabd64-0c7b-cc0e-dd9c-ed0a295316c1.png)

# おわりに
以上、PlantUMLのちょっとしたショートカットキーメモでした。
ちなみに、PlantUMLの書き方は下記記事がとても参考になります。
[PlantUML Cheat Sheet](https://qiita.com/ogomr/items/0b5c4de7f38fd1482a48#use-case)
Firebase(GCP)のプロジェクトを保護する


お手軽につかえるFirebaseですが、お手軽に削除するといやなので保護の方法をチェック。
GCPの方でLien（リーエン）というのを使ってプロジェクトを保護する方法があるのは知っていましたが、Firebaseで使えるのかチェックします。
2019年12月現在ではUIではできないみたい。

>Lien: 先取特権、留置権という意味らしい。知らんかった。

## 準備

gcloudコマンドを使うので、インストールがまだならインストールしておく。
で、適切なアカウントでログイン。以下のコマンドを利用するとWebが起動し、アカウントを選択できる。

```
gcloud auth login
```

管理下のプロジェクトのリストを確認。

```
gcloud projects list
```

どのプロジェクトがデフォルトになっているか等を確認。

```
gcloud config list
```

必要なら操作対象のプロジェクトを設定。

```
gcloud config set project [PROJECT_ID]
```


## Lien（リーエン）の設定

選択されたプロジェクトにLienを設定してみます（--projectで明示することも可能みたい）。

### 作成

2019年12月現在、制限できるのはdeleteのみらしい。まあ用は足ります。

```bash
gcloud alpha resource-manager liens create \
> --restrictions=resourcemanager.projects.delete \
> --reason="Important Project"
```

### 確認

設定されたか確認。

```bash
gcloud alpha resource-manager liens list
```


### 削除

今は削除しませんが、後々。


```bash
gcloud alpha resource-manager liens delete [LIEN_NAME]
```


## 動作確認

Firebaseにてプロジェクトを削除してみる。
下記のような画面がでて削除できません。成功です！


![スクリーンショット 2019-12-04 5.20.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/0136594c-9522-c599-5a09-f48f77928de5.png "スクリーンショット 2019-12-04 5.20.32.png")

これで不用意に本番プロジェクトを消すことは防止できそうです。
以上です。
GCPの地域制限と、GAE 2nd genのデプロイで失敗した話

# 言いたいこと

* 組織ポリシーで、GCPのリソースの地域を制限することが出来るよ
* GAEの2nd gen(Python3.7とか)には、マルチリージョンなバケットが必要だよ
* ので、地域制限するなら、マルチリージョンな単位（eu/us/asia）でした方がいいよ


# 組織ポリシーによる地域制限の話
組織ポリシー（organization policy）で、GCPのリソースが[作られる場所を制限することが出来ます](https://cloud.google.com/resource-manager/docs/organization-policy/defining-locations)。


## GCPサービスに与える影響
組織ポリシーが、各GCPサービスごとに与える影響は、こちらのページに[書いています](https://cloud.google.com/resource-manager/docs/organization-policy/defining-locations-supported-services)。
例えば：

* Bigtableはクラスターが作られるゾーンを限定（既存のクラスターには影響しない）
* GCSはバケットのリージョン・マルチリージョンを限定（既存のバケットには影響しない）
* GCEの場合はちょっと複雑
  * インスタンスの作成はリージョンで制限される
  * CloudDNSなどは、地域制限の対象外


## 地域の指定
ゾーン（asia-northeast1-aとか）、リージョン(asia-northeast1とか)、マルチリージョン(asiaとか)で指定することも出来ますが、[ValueGroup](https://cloud.google.com/resource-manager/docs/organization-policy/defining-locations#value_groups)(asia-northeast1-locationsとか)で指定する方が良いです

なぜなら：

* Googleが関連するゾーン・リージョンを加えてくれる
* リージョンだけを許可すると、ゾーンで立つGCP サービス（GCEとかDataflow）は使えない
 * （私はasia-northeast1だけを許可して、Dataflowの起動に失敗しました）

ためです。

# GAEの2nd genのデプロイで失敗した話
* Python3.7
* 組織ポリシーでasia-northeast1関連のみ許可(asia-northeast1-locations)
 * この設定では、マルチリージョンなバケット(usとかasia)は作られない

で試すと、GAEをデプロイするときに失敗しました[(issue)](https://issuetracker.google.com/issues/145315190)。

なんで失敗するかと言うと、

1. GAEの2nd generationは、**Standardでも**デプロイ時にContainer Registryを使用
2. Container Registryはマルチリージョンなバケットを[使う]
(https://cloud.google.com/container-registry/docs/overview?hl=ja#registry_name)
3. そのため、組織ポリシーでマルチリージョンなバケットを制限しているとコケる

からです。
ちなみに、1st gen(Python2.7)では地域制限をしていても使うことが出来ました。
（[2nd genでgVisorベース](https://cloud.google.com/appengine/docs/standard/runtimes?hl=ja)になった影響？）



# じゃあ地域制限って使えないの？

* マルチリージョンなバケット作ることが出来る制限(europe-locationsやus-locations)であれば…
* Pub/Subは、組織ポリシーとは別に[地域制限を設定することが出来ます](https://cloud.google.com/pubsub/docs/resource-location-restriction?hl=ja)
pytubeを使ってみよう

#[IPFactory Advent Calender 2019](https://qiita.com/advent-calendar/2019/ipfactory)  4日目
大層な記事を書けるほどの知識はありませんが、4日目書かせていただきます。
IPFactory所属、ISC 1年の[pycys](https://twitter.com/PycysA)です。

##pytubeとは
Pythonのライブラリです。
YouTube上の動画を簡単にダウンロードできます。

##環境
- Windows 10 Pro
- Python 3.7.4

##インストールしよう
```
pip install pytube
```
上記をbashなりコマンドプロンプトなりに打ち込めばインストールされます。

#注意
ダウンロードする前に動画の投稿者のチャンネル名を見てください。
チャンネル名の横にチェックマークがついていない場合、
ダウンロードしたらYouTubeの規約に違反してしまいます。

##動画をダウンロードしてみよう
早速ダウンロードしてみましょう。

```python:download1.py
from pytube import YouTube

YouTube(input("ダウンロードしたい動画のURL:")).streams.first().download()
```
上記のコードをコピペして実行してみてください。
URL聞かれたら動画のURL貼り付けてEnter。
正常に動作した場合、同じフォルダに動画ファイルができたはずです。

##保存するフォルダを指定する
```python:download2.py
from pytube import YouTube

YouTube(input("ダウンロードしたい動画のURL:")).streams.first().download(r"フォルダのパス")
```
保存するフォルダを指定したい場合は、downloadメソッドの引数に保存先フォルダのパスを渡しましょう。
フォルダパスを指定するので、rをつけてエスケープシーケンスを無効化するのも忘れずに。
もしダウンロードする時に動画ファイルの名前を変更したい場合は、downloadメソッドの第二引数に付けたい名前を設定しましょう。

##解像度やフォーマット等を指定してみる
```python:download3.py
from pytube import YouTube

url = input("ダウンロードしたい動画のURL:")
print(*YouTube(url).streams.all(), sep="\n")
itag = int(input("ダウンロードしたい動画のタグ:"))
YouTube(url).streams.get_by_itag(itag)).download()
```
今度はこちらのコードを実行してみてください。
実行した直後にURLを求められるので、入力してEnter。

するとこんな感じの文字列が返ってきましたね？
(フォーマットが似ていれば大丈夫です)

```
<Stream: itag="22" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.64001F" acodec="mp4a.40.2">
<Stream: itag="43" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp8.0" acodec="vorbis">
<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">
<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028">
<Stream: itag="248" mime_type="video/webm" res="1080p" fps="30fps" vcodec="vp9">
<Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f">
<Stream: itag="247" mime_type="video/webm" res="720p" fps="30fps" vcodec="vp9">
<Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401f">
<Stream: itag="244" mime_type="video/webm" res="480p" fps="30fps" vcodec="vp9">
<Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e">
<Stream: itag="243" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp9">
<Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015">
<Stream: itag="242" mime_type="video/webm" res="240p" fps="30fps" vcodec="vp9">
<Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c">
<Stream: itag="278" mime_type="video/webm" res="144p" fps="30fps" vcodec="vp9">
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">
<Stream: itag="249" mime_type="audio/webm" abr="50kbps" acodec="opus">
<Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus">
<Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus">
```
これらはダウンロード可能なフォーマットや解像度等の組み合わせであり、
この中からダウンロードしたい組み合わせのitagの値を指定することでダウンロードできます。

返ってきた組み合わせから良さげなものを選んで、itagの値を入力してみましょう。
入力した値が間違っていなければ、選んだファイルがダウンロードされたはずです。

##最後に
以上になります。
誤った情報や誤字脱字、分かりにくい点等ありましたらご指摘ください。
最後まで読んでいただきありがとうございました。
ドキュメンテーションツール

システム開発時の設計書作成を簡単に

## 今回対象とする設計書と使用ツール
1. ER図
 - [A5M2](http://a5m2.mmatsubara.com/)
 - [MySQL Workbench](https://www.mysql.com/jp/products/workbench/)
- テーブル定義書
 - [A5M2](http://a5m2.mmatsubara.com/) 
 - [MySQL Workbench](https://www.mysql.com/jp/products/workbench/)
- 詳細設計書
 - [phpDocumentor](https://www.phpdoc.org/)
 - [phpDox](http://phpdox.de/)
 - [Doxygen](http://www.doxygen.jp/)
- API仕様書
 - [Swagger](https://swagger.io/)
- 単体、結合テスト
 - [PHPUnit](https://phpunit.de/)
【iOSアプリ開発メモ】JSON Serverを使ってiOSアプリ開発する時のATS無効化手順(Xcodeのキャプチャ付き)

# はじめに
まだ完成していないWebAPIを利用してiOSアプリ開発を行う必要があり、その際、ローカルPCにAPIモックサーバー(Node.jsのJSON Server)を立てて開発を進めました。
通常だとiOSアプリは**ATS(App Transport Security)によりHTTP通信が利用できない**ため、上記作業を行う際に「**ATS無効化の設定**」を行いました。本記事では、その時の手順をXcodeのキャプチャ付きでメモしておきます。

# 前提
- Xcode Version 11.2 
- iOSアプリからは「localhost」でAPIサーバーへアクセスする
- (下記のXcodeのキャプチャはSingle View Appでprojectを作成した直後の状態)

# ATS無効化手順(1から8まで)
1. XcodeでInfo.plistを開く
![スクリーンショット 2019-12-04 4.08.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145054/a1b9a904-b589-02ca-15aa-9cad3293cd71.png)


2. App Transport Security Settingsを追加する
![スクリーンショット 2019-12-04 4.10.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145054/e6a12f46-c460-3207-add0-7949234d6eb8.png)


3. Allow Arbitrary Loadsを追加し、ValueはNOとする
※ 2で追加したApp Transport Security Settingsの子として追加する(手順4も同様)
![スクリーンショット 2019-12-04 4.32.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145054/409083fc-0959-7619-6588-dc051389a9d7.png)


4. Exception Domainを追加する
![スクリーンショット 2019-12-04 4.37.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145054/3bf42bbc-2baf-949e-e926-80b0d89f4c44.png)


5. localhostを追加する
※ 4で追加したException Domainの子として追加する
※ TypeはDictionaryにする
![スクリーンショット 2019-12-04 4.39.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145054/b87a8c3e-3747-789e-3fb6-02a0393dddc8.png)


6. NSIncludesSubdomainsを追加し、ValueはYESとする
※ 5で追加したlocalhostの子として追加する(手順7, 8も同様)
※ TypeはBooleanにする(手順7, 8も同様)
![スクリーンショット 2019-12-04 4.42.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145054/0affe7ec-cf9a-ac1b-bf21-259a0fa4a6ee.png)


7. NSTemporaryExceptionAllowsInsecureHTTPLoadsを追加し、ValueはYESとする
![スクリーンショット 2019-12-04 4.45.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145054/d6a1ea13-6ca6-daf6-1709-b6c2f9bfcc97.png)

8. NSTemporaryExceptionRequiresForwardSecrecyを追加し、ValueはNOとする
![スクリーンショット 2019-12-04 4.49.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145054/1e5e53c9-3da5-d7d8-ef6b-92c539ff1063.png)

ここまでの設定を行えば、HTTP通信でlocalhostにアクセスできる

# 参考リンク
JSON Serverを立てる時や、ATS無効化手順については下記記事を参考にしました。

- [今更使うJSON Server](https://qiita.com/Lurium/items/313f8f770a710b5ed188)
- [[iOS] ATS対応徹底攻略！対応策とInfo.plistより行うATSの設定値まとめ](https://dev.classmethod.jp/smartphone/iphone/ios-ats-cheats-info-plist-settings/#what-is-ats)
webアプリの勉強がてらサークルの内部システムを作った話

# TL;DR
- サークルでの情報共有がめんどくさかったので、勉強がてら内部向けwebシステムを作った
- フレームワークというものの存在を知らなかったのでフルスクラッチで作ったところメンテナンス性がなさすぎた
- フレームワークを使うべき

# あらすじ
詳細はサークルの内部情報に関わってくるので言えませんが、ファイルで管理していた情報が管理しきれなくなってきたということで、自分で内部向けwebシステムを開発することとなりました。
簡単に言えば[kintone](https://kintone.cybozu.co.jp/)のようなものを自作しようとしたわけです。

構成は以下のような感じです。

- PHP + MySQL
- フレームワークなし（フルスクラッチ）
- ところどころjQueryを使っている

# 勉強しながらの開発
開発を始める前はwebアプリなんて書いたことがなく、ほぼ一からの勉強でした。

1. PHP、SQL、HTML、CSSの書き方
2. httpプロトコルのお勉強
3. セキュリティの勉強

を勉強しました。言語の勉強だけでなく、2. や3. の勉強をしなければいけないのがwebプログラミングのつらさですね。
また、初めての開発ということもあり、セキュリティ関係は相当慎重になりました。（その分使い勝手は悪化していますが）
最終的には合計5000行ほどのコードになりました。

### 身についた知識
- HTML, CSS ,JavaScript, PHPなどの言語仕様
- 最新の記法や命名規則
- 流行りのフレームワーク

この開発を通して様々な知識をつけることができました。

わからないことが多く、ひたすらググりました。ひたすら公式ドキュメントを読みました。
また、流行にも敏感になるようになりました。

よく言われるように、プログラミングの上達の近道は一つでも多くのプロダクトを開発し、アウトプットをすることだな、ということをこの開発で実感しました。

### ダメだった点
- フレームワークを使わなかった
- モデルを知らなかった（MVC？MVVM？何それおいしいの？）
- そもそも「フロントエンドとバックエンドの分離」とかいう発想がなかった
- CSSの構成がグチャグチャ（途中でSMACCSに変えようとしたりBEMにしようとしたり)
- 命名規則バラバラ
- その結果、自分以外がメンテナンスするのが困難なコードが生み出された
- なんなら自分でもわからない

実装しながら勉強したため、基本的な設計に関してはほとんど知識のない状態で行ってしまいました。勉強して知識がついたころにはもう実装がだいたい終わっており、設計を変えるのはもはや不可能という状況でした。

# これから
本当は一から作り直したいレベルのアプリケーションでしたが、スケジュールの予定でいち早く運用開始する必要があったため、もう運用されています（そろそろ運用されて1年が経ちますね）。
僕ももう引退が視野に入っているので下の代に引き継がなければいけないのですが、このままのコードでは引き継げません（汗）

間を縫ってデータ移行の準備を進めながら、また一から実装するかもしれません。（せっかく作ったwebシステムは使い続けて欲しいですからね）

# まとめ
色々と問題点は挙げましたが、特に重大な問題は発生せず運用できています。

また、自分のいい経験になる開発でした。
大きなプロダクトを作るのは楽しいものです。プログラミングを学び始めた人や少しでも上達したいと思っているかたも、ぜひ何か実用的なものを開発してみてください。きっと楽しさに目覚めるはずです。
ちなみに、今度は自分用のToDoアプリを作ろうと思っています。もしかしたら記事にするかもしれないのでお楽しみに。
SageMaker発表まとめメモ

re:Invent 2019のKeynoteでSageMakerに関する怒涛のアップデートがあったので追いきれず。。。発表されている内容を後から見返すためのメモ。

# Keynoteで発表のあったもの
SageMaker Studio：WebベースのSageMaker IDE
https://aws.amazon.com/blogs/aws/amazon-sagemaker-studio-the-first-fully-integrated-development-environment-for-machine-learning/
SageMaker Notebooks：ノートブックをインスタンスの起動なしに立ち上げられる
SageMaker Experiments：パラメータをキャプチャして入力パラメータをキャプチャし設定が可能
https://aws.amazon.com/blogs/aws/amazon-sagemaker-experiments-organize-track-and-compare-your-machine-learning-trainings/
SageMaker Debugger：メトリクス収集自動化？
https://aws.amazon.com/blogs/aws/amazon-sagemaker-debugger-debug-your-machine-learning-models/
SageMaker Model Monitor：デプロイされた本番モデルのモニタリングが可能
https://aws.amazon.com/blogs/aws/amazon-sagemaker-model-monitor-fully-managed-automatic-monitoring-for-your-machine-learning-models/
SageMaker Autopilot　：分類からトレーニングからお任せ的な？
https://aws.amazon.com/blogs/aws/amazon-sagemaker-autopilot-fully-managed-automatic-machine-learning/

# その他
Keynoteでは発表がなかったけど、こっそりAWSのブログで発表されていたのがこちら。

Amazon SageMaker Processing – Fully Managed Data Processing and Model Evaluation
https://aws.amazon.com/blogs/aws/amazon-sagemaker-processing-fully-managed-data-processing-and-model-evaluation/
Dell PowerConnect を使うならこれだけは知っておけ

PowerConnect 6248 で検証しています．
`hayabusa` というのは今回使用したスイッチの hostname なので気にしないでください．

PowerConnect のコマンドは Cisco IOS っぽい雰囲気を醸し出しています．

# 準備
- telnet でログイン

```
$ telnet hayabusa
Trying 192.168.10.1...
Connected to hayabusa.
Escape character is '^]'.

User:admin
Password:************
```

これだけでいいのは `/etc/hosts` に以下のような記述があるからです．

```/etc/hosts
192.168.10.1   hayabusa
```

- 特権モードに入る

```
hayabusa>enable
```

- 現在の設定を見る

```
hayabusa#show running-config
!Current Configuration:
!System Description "PowerConnect 6248, 3.3.18.1, VxWorks 6.5"
!System Software Version 3.3.18.1
!Cut-through mode is configured as disabled
!
configure
vlan database
...
```

# インタフェースの設定

- 設定モードに入る

```
hayabusa#configure
```

- インタフェース (例: 1 番ポート) に入る

```
hayabusa(config)#interface ethernet 1/g1
```

- インタフェースの設定をする (例: 説明の追加)

```
hayabusa(config-if-1/g1)#description "eth0.hogehoge"
```

- インタフェースを抜ける -> 設定モードを抜ける

```
hayabusa(config-if-1/g1)#exit

hayabusa(config)#exit
```

# VLAN の設定
- 設定モードに入る

```
hayabusa#configure
```

- VLAN データベースに入る

```
hayabusa(config)#vlan database
```

- VLAN を追加する (例: vlan 3001)

```
hayabusa(config-vlan)#vlan 3001
```

- VLAN を削除する (例: vlan 3001)

```
hayabusa(config-vlan)#no vlan 3001
```

設定を消す時は大体 `no` をつければ大丈夫．

- VLAN データベースを抜ける

```
hayabusa(config-vlan)#exit
```

- VLAN インタフェースに入る

```
hayabusa(config)#interface vlan 3001
```

- VLAN インタフェースを設定する (例: 名前の追加)

```
hayabusa(config-if-vlan3001)#name vlan-hoge
```

- VLAN インタフェースを抜ける -> 設定モードを抜ける

```
hayabusa(config-if-vlan3001)#exit

hayabusa(config)#exit
```

# 終了時

- 現在の設定を保存する

```
hayabusa#copy running-config startup-config
```

- 特権モードを抜ける -> 終了する

```
hayabusa#exit

hayabusa>exit
```

# 豆知識

- ヘルプを表示する

```
hayabusa#?

arp                      Purge a dynamic or gateway ARP entry.
boot                     Marks the given image as active for subsequent
                         re-boots.
captive-portal           Configure Captive Portal privileged exec parameters.
```

コマンド名を忘れたらとりあえず `?` を打ちましょう．
また，入力中に tab を押せばコマンド名が補完されます．
PDFから文字の読み取り処理する時の検討事項



データはないのか？
　元からないか？
　買えないか？
　似たものはないか？

pdfから変換はできないか？
 pdf　→　word
 pdf　→　text
 など

※ライブラリを試すときは何個か試してみること。
ライブラリによっては簡単にできる可能性もあり。
例えばpdf詳解に乗ってるような細かいフォーマットを知らないと
実装ができないという思い込みは禁物。
ライブラリを変えただけで文字化けが直るなど、
理解が足りなくてもすぐに解決する場合もある。
もちろん時間があるときに細かく理解する努力は必要。
pdfのライブラリを書いてるようなgitのプロジェクトに入るか、
海外エンジニアのメンターのサービスを使って質問すると良い。

クローリングはできないか？
　とって来れそうなサイトはないか。

OCRはできないか？
　複合機や、既存のソフトウェアでは対応できないか？
　紙に印刷すると簡単にできないか？
　Google, Azure, Awsのapiを使えないか？
　　※枚数が多いと金額が高いので注意。個人的に考えずに使って60万くらい溶かした。クレジットリボ払いにした。涙
　Tesseract OCRなどのライブラリはどうか？
　　精度はあまり良くないが悪くもない。
　独自アルゴリズム実装のpocをやらせてもらえないか？

機能自体代用できないか？
など
自動デプロイ実行中にunicornが起動しない(SSHKit::Runner::ExecuteError)

# この記事は初学者がエラーになったときの対処法を記載したものになります。
同じ初学者の方は、一度記事を全部読んで同じ症状に当てはまる場合実行してみてください。

## 現状
現在私は、１つのrailsアプリケーションを作り、AWSのEC2、Mysql、Nginx、Capistrano等を使い自動デプロイを行う際に置きたエラーになります。
また、私は、このアプリケーションで一度自動デプロイを成功させておりますので、初めて自動デプロイを行った際のエラー内容ではありません。

## エラー内容

```html:ローカル環境
bundle exec cap production deploy
```
自動デプロイコマンドを実行後unicornが起動しない

```html:エラーコマンド一部

bundle exit status: 1 (SSHKit::Runner::ExecuteError)

Caused by:
SSHKit::Command::Failed: bundle exit status: 1

Tasks: TOP => unicorn:start
(See full trace by running task with --trace)
```

##解決策
まず、unicornの環境変数がしっかりと定義されているか確認を行いましょう。
変数の確認するためのコマンドがいくつかあります。

```
printenv
env | grep SECRET_KEY_BASE
env | grep DATABASE_PASSWORD

```

調べてばもっと出てきます。

上記コマンドを行い定義されているかしっかり確認しましょう。

##原因

私の場合一度自動デプロイを行いましたが、違うエラーが起きてしまい、その際に環境変数を変えてしまったと言うことになります。
環境変数の方法が間違っていた。
みなさまも、定義した場合しっかりと定義できているか確認する癖をつけましょう。
nuxt.jsで現在のウインドウサイズをどのコンポーネントからでもリアクティブなオブジェクトとして取得できるプラグインのつくりかた

#はじめに
スマホに対応中に、要素の有無をcssの`@media`指定と`display:none`じゃなくて`v-if`で操作したいけどめんどくさそうだから楽にしたいという欲求からプラグインの作り方から勉強したやつ。
***※注 nuxt.jsのUniversalモードを使用している場合は、このアプローチだと表示崩れを招く原因となるので、素直にCSSかuser-agentで表示切り替えを行いましょう***

#とりあえずコード

```js:plugins/window-resize.js
import Vue from 'vue'

Vue.use({
  install(Vue) {
    const $window = Vue.observable({
      width: 0,
      height: 0,
    })
    let queue = null
    const wait = 100

    const getWindowSize = () => {
      clearTimeout(queue);

      queue = setTimeout(function () {
        $window.width = document.documentElement.clientWidth
        $window.height = document.documentElement.clientHeight
      }, wait);
    }

    global.addEventListener('resize', getWindowSize)
    getWindowSize()

    Vue.prototype.$window = $window
  }
})
```

```js:nuxt.config.js
plugins: [
  {src: '~/plugins/window-resize.js'}, 
],
```
#せつめい
`install(Vue)` - Vueのプラグインを作るときに必須のメソッド。詳しくは[プラグインの記述]
(https://jp.vuejs.org/v2/guide/plugins.html#%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%AE%E8%A8%98%E8%BF%B0)(公式ドキュメント)
`Vue.observable(object)` - vue.jsおなじみのdata関数の戻り値にも適用されているメソッドで、このメソッドに処理されることによって、戻り値にリアクティブオブジェクトが得られる。[Vue.observable( object )](https://jp.vuejs.org/v2/api/index.html#Vue-observable)(公式ドキュメント)
`setTimeout` - resizeのイベントリスナーは数pxリサイズされる毎に発火され、どこからでも使える割に鬱陶しいので上記サンプルでは0.1秒スクロールが止まるまでは発火を抑制するようにしています。
`global.addEventListener('resize', getWindowSize)` - ここで、どこで画面をリサイズしても、ウインドウサイズが取得されるようにしている。
`Vue.prototype.$window = $window` - ここでどこからでも`this.$window`で使えるようにしている。

#おわりに
`<div v-if="$window.width > 1080">おつかー！</div>`みたいな感じで表示を司ることができました！
vueプラグインなるものを初めて作ってみたのですが、`Vue.observable`君が便利ですね・・・
今後もいろいろ作って引き出し増やして行きたいと思ってます！
Outsystemsで非同期処理を実装する方法

# はじめに
転職して入った会社のITメンバーでアドベントカレンダーに参加するということで、
初めてQiitaに記事を投稿します！
転職してからOutsystemsでアプリ開発をしており、
開発を進める中で調べてもなかなか答えにたどり着けなかったことをシェア出来ればと思います。

今回は、Outsystemsで非同期処理を実装する方法について紹介します。

# 自己紹介
初めて投稿するので、自己紹介しておきます。
以前は30名程度のSIerで様々なプロジェクトに常駐するSEとして7年ほど働いていました。
その頃はJavaやC#で開発をしたり、要件定義や設計などの上流工程もやったりしていました。
そこから外資系の人材会社に転職し社内SEとなりました。
現在は社内の業務効率化のためのアプリなどを主にOutsystemsで開発しています。

# 目次
- 非同期処理とは
- クライアント処理での実装方法
- サーバ処理での実装方法

# 非同期処理とは
そもそも非同期処理とは？　という方のために説明します。
と思ったのですが、説明してくれてる記事があったのでこちらを参考にしてください。
https://qiita.com/kiyodori/items/da434d169755cbb20447

例えば、Webページでボタン押したときに画面ぐるぐるの時間が長いと
ユーザビリティが低くてよろしくないので、
処理結果を待つ必要がない処理は非同期で動かそうよってことですね！

JavaだとThreadを継承したクラスやRunnableを実装したクラスをnewして
startメソッド呼び出せば実現する感じですが、
Outsystemsだとどう実現するかがなかなか見つからなかったので、シェアしたいなと思いました。

では、本題行きます！

# クライアント処理での実装方法
クライアント処理では、Javascript の setTimeout が使えます。
例えばこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380120/94ada535-88c5-c8b3-626d-4a45843e970c.png)

上の画像はなにをしているかというと、
スマホやタブレットのバッテリーの状態をみて、残りが少ないとアラートメールを送信する
NotifyBatteryStatusというクライアントアクションを作っており、
それをsetTimeoutで呼び出しています。

`setTimeout($actions.NotifyBatteryStatus($parameters.Level, $parameters.IsPlugged), 0);`
setTimeoutの第１引数が非同期で処理させたいアクション（引数付き）で、
第２引数が何ミリ秒後に実行するかです。今回は即実行させたいので0にしてます。

メール送信はサーバアクションを呼ぶしかないので、ネットワークを介する分処理に時間がかかりますが
その処理結果を見る必要がなかったので、
メールを送るか判定する部分も含めて非同期で実行するようにしています。

というわけでクライアント処理で非同期させたかったら、setTimeout使えます！
これはJavascript経験者ならすぐ出てきそうですね。

# サーバ処理での実装方法
さて、クライアント処理はJavascript使えば行けるっていうのはすぐたどり着けるのですが、
サーバ処理での実現方法はOutsystems特有で、調査に時間かかりました。
で、結論から言うと２つのやり方があります！

- Timer
- Process

### Timerを使った非同期処理（引数指定不可）
Timerってスケジュール起動か手動実行するバッチ処理だよね？って感じですよね。
そのTimerをサーバアクションから呼べるんです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380120/7c27e6c0-86c6-7cbc-da00-6d1072a3e0c9.png)

こんな感じで、
Timerの下にあるWake～をサーバアクション内にドラッグ＆ドロップすればTimer処理が呼べちゃいます。
で、それは実行しているサーバアクションとは非同期で動いてくれるんです。
Wake～を呼んだら即実行されるわけではなくて、数秒たってから実行されます。

ただ、Timerでの非同期処理の難点としては、パラメータが渡せないところです。
それを解決してくれるのが次です。

### Processを使った非同期処理（引数指定可能）
Processって使ったことありますか？私はなかったです。。
こんな使い方ができるんだなって感じでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380120/7178bd25-ef41-57db-7385-dc95d6788254.png)

ProcessのLaunch～をサーバアクションから呼び出せて、引数も指定できます。
Entityは無理ですが、TextやIntegerなどのBasicTypeと、EntityIdentifierが引数として使えます。
なのでEntityIdentifierを引数に渡して、
Process内でEntityからデータを取得して処理するっていうのはできますね。

# 最後に
というわけで、Outsystemsでの非同期処理の実装方法を紹介しました。
昨今SaasがいろんなAPIを公開してたりしますが、それをRESTやSOAPで呼び出すときに時間がかかるので、
そういった処理を非同期で呼ぶようにするなんてこともよくあるかなと思いますので、
ぜひ参考にしていただけると嬉しいです！
HTMLの基本要素について

##はじめに
HTML5の基本要素について、備忘録も兼ねて整理をしてみた。

##HTMLの基本構造
要素  |意味
---|---
DOCTYPE  |HTMLのバージョン情報を宣言する。
html  |HTML文書のルートを表す。全ての要素はhtml要素の中に記述する。
head  |HTML文書自体の情報を表す。html要素の最初の要素として記述する。
meta  |文章に関する情報を表す。
title|HTML文書の表題を表す。ブラウザのタイトルバーに要素の内容が表示される。 
body|文章の内容を記述する領域を表す。

##HTMLの代表的な特殊文字
HTMLの中に半角の<や>を入れるとHTMLタグの一部と認識されてしまい表示されない。
なので表示するために、特殊な書き方をする必要がある。

| ブラウザでの表示 | コード上の書き方 | 名前 |
|:-:|:-:|:--|
| < |\&lt; | 小なり |
| > |\&gt; | 大なり |
| & |\&amp; | アンパサンド |
|   |\&nbsp; | 空白 |
| © |\&copy; | 著作権記号 |

##ページ全体(body)の構造を表現するタグ

HTML5から追加された文書を構造化しやすくするためのタグ。
構造化することで検索エンジン(Google)のクローラーがコンテンツ内容を認識しやすくなり、
クローラビリティがあがり、検索エンジンに評価されやすくなる。

要素  |意味
---|---
nav |ページのナビゲーションを表示する
header |コンテンツのヘッダー部分に使用
article |独立したコンテンツであることを示す
section |コンテンツの中の一つの区切りを示す
aside |メインコンテンツとは別にサブに表示するタグ
footer |コンテンツのフッター部分に使用
h1〜h6 |見出し
p |段落

- `header`と`footer`はページ内で１つだけしか使えないというわけではなく、一覧ページの１つ１つのコンテンツを囲む際にも使うことができる。
- 内容が異なるものを区別する場合は、`article`タグを使う。
- 関連している内容を区別する場合は、`section`タグを使う。
- 厳密にどちらかわからない場合は大人しく`div`タグを使う。

構造化データのSEO効果については[構造化データとは？マークアップ方法からSEO効果まで解説！](https://seolaboratory.jp/95442/)に詳しく解説されていたので、こちらをご確認ください。

####参考記事
>[HTML のセクションとアウトラインの使用](https://developer.mozilla.org/ja/docs/Web/Guide/HTML/Using_HTML_sections_and_outlines#Defining_headings)
[HTML5の新規タグ「section、article、header、footer」の使い方](https://sole-color-blog.com/blog/65/)
We're sorry, but something went wrong.

# この記事は初学者がエラーになったときの対処法を記載したものになります。
同じ初学者の方は、一度記事を全部読んで同じ症状に当てはまる場合実行してみてください。

## 現状
現在私は、１つのrailsアプリケーションを作り、AWSのEC2、Mysql、Nginx、Capistrano等を使い自動デプロイを行う際に置きたエラーになります。

## エラー症状
自動デプロイ後　IP　で繋いだ所下記の画面が表示される。
<img width="764" alt="スクリーンショット 2019-12-04 1.47.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480998/0f3e4a85-74f1-c1be-55ab-e280aea15682.png">

##原因

私の場合、デプロイ後に

```html:サーバー環境
kill -QUIT `cat tmp/pids/unicorn.pid` 
```

上記のコマンドを訳もわからずに実行した結果表示されました。
（上記コマンドは、ユニコーンの停止を行うコマンドです。）

## エラー解説

We're sorry, but something went wrong.
If you are the application owner check the logs for more information.
とは、

```html:ローカル環境
bundle exec cap production deploy
```

を行いましたが、unicornもしくはnginxにエラーが起こっている場合に起こる可能性のあるエラーになります。
私の場合デプロイを行ったのにもかかわらず、unicornサーバーを落とした為エラーになりました。
また、このエラーはlogに残らない場合もあります。
##解決策
エラーの原因がunicorn,Nginxどちらにあるかわからない為、両方を再起動させましょう。

まず、unicornのサーバーがまだ可動しているか確認を行います。

```html:サーバー環境
ps aux | grep unicorn
(unicornのサーバー状態を調べるコマンド)
実行後↓
ec2-user  7263  0.0  8.6 335420 87328 ?        Sl   16:34   0:02 unicorn master               
ec2-user  7341  0.0  9.6 349224 97032 ?        Sl   16:34   0:00 unicorn worker[0] 
ec2-user  7763  0.0  0.2 110540  2092 pts/3    S+   17:14   0:00 grep
※見られたくない部分もある為カットしている部分あります。

サーバーが停止している場合１行しか出てこないです。
```

unicorn masterの行にある左から2番目の番号（７２６３）を

```html:サーバー環境
kill 7263
kill -9 7263
どっちでも良い
```
にて停止してください。
その後

Nginxを再起動させます。

```html:サーバー環境
sudo service nginx reload
（リロードさせるコマンド）
実行後↓
Stopping nginx:                                            [  OK  ]
Starting nginx:                                            [  OK  ]
```

※下記コマンドは行わなくても大丈夫です。
ついでにMysqlも再起動させときます。

```html:サーバー環境
sudo service mysqld restart
（リロードさせるコマンド）
実行後↓
Stopping mysqld:                                           [  OK  ]
Starting mysqld:                                           [  OK  ]
```

その後

```html:ローカル環境
bundle exec cap production deploy
（自動デプロイさせるコマンド）
```

以上でエラーは解除されると思います。
TabBarControllerをStoryboardReferenceを利用して導入してみた(Storyboardを分ける)

## 状況
[Youtubeチャンネル](http://www.youtube.com/channel/UCEZIDajfDXXZtosEn9u94Ug?sub_confirmation=1)の素材用に[Tinderの模擬アプリ](https://www.youtube.com/watch?v=RaKaGPsUU9w)を作成する際、
`TabBarController`を使ったのですが
やり方が色々あってこんがらがったのと、いくつかつまづいたのでメモ。

#### やりたいこと
`TabBarController`を`StoryboardReference`を利用して導入する
->Storyboardを`TabBarController`, `遷移先のVC`(Tabの数ぶんStoryboardを用意 or 遷移先を1つのStoryboardにまとめる)でそれぞれ分けたいので`StoryboardReference`を利用
->遷移先のVCに`NavigationController`を継承させたい

## 対策
- `TabBarController.storyboard`を新しく作る->`TabBarController`を新しく作って、つながっている2つのVCを消しておく
- `StoryboardReference`をTabの数だけ作る
- 各々の`StoryboardReference`をTabと紐付けたい遷移先のVCと紐付ける
- TabBarControllerの`is initial viewcontroller`にチェックを入れる
- `TARGET`の`Main interface`を**TabBarControllerが存在するstoryboard**に設定
- `info.pist`の`Storyboard Name`を**TabBarControllerが存在するstoryboard**に設定
- 以上を行ってもTabが表示されない場合はcleanや再起動諸々を試す(info.plistとかをいじるとcleanとかで解決するパターンが結構あるらしい。僕自身もcleanして解決しました。)
![スクリーンショット 2019-12-16 0.07.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427937/315b0e60-9dd3-34c5-4f23-175557a50441.png)


**※一番左のタブから順々に紐づいていく**

**- 遷移先のVCごとに`Storyboard`がある場合:**
`StoryboardReference`を選択し`Show the Attributes inspector`->`Storyboard reference`->`Storyboard`に**Storyboardの名前(ファイル名)を入力**(`Storyboard ID`ではない)
**- 遷移先のVCを1つの`Storyboard`にまとめている場合:**
上記に加えて、**VCをStoryboard IDから参照が必要**->`Show the Identity inspector`->`Identity`->`Storyboard ID`に入力


- control+右クリックで各々の`StoryboardReference`に繋ぐ->`Relationship Segue`の`view controllers`を選択
(※`Modal`などではない)
- 遷移先のVCで`Is Initial Viewcontroller`の設定を確認(NCが紐づいているならそちらに設定する)
- 遷移先のVCで`Is Initial Viewcontroller`に設定したものに`TabBar Item`を挿入

以上。

## 参考
**・[[Xcode7] 「Unknown class xxxxx in Interface Builder file.」が出る - Qiita](https://qiita.com/eidera/items/f6fd3b1088d7bd6fe523)**
KotlinのSequenceとListの挙動の違いについて

# KotlinのSequenceとは

Sequenceとは、Kotlinの標準ライブラリにある遅延リストを表す型です。
Listと似たメソッドを持ちますが、その実行自体は遅延されるというやつです。
こういうやつです。

```kotlin
val numberSeq: Sequence<Int> = (1 until 10).asSequence()
val resultSeq: List<Int> = numberSeq
    .map { it * 2 }
    .filter { it % 3 == 0 }
    .toList()
println(resultSeq) //6, 12, 18
```

参考までにSequenceの代わりにListを使って同じ処理を行った場合のコードも載せます。
**ListとSequenceの間で、利用できるメソッド等ほぼ違いがないことがわかるかと思います。**

```kotlin
val numberList: List<Int> = (1 until 10).toList()
val resultList: List<Int> = numberList
    .map { it * 2 }
    .filter { it % 3 == 0 }

println(resultList) //6, 12, 18
```

このSequenceのListとの内部挙動の違いについて考えてみたいと思います。

# 違い1: Sequenceは実行がステップごとではなく要素ごと

**ListはStep-by-stepに, SequenceはElement-by-elementに実行される**という大きな違いがあります。

意味不明だと思うのでコードを使って説明します。

### List: Step-by-stepの挙動

以下のコードだと…

```kotlin
//List example
val numberList = (1..3).toList()
val resultList = numberList
    .map { println("1st map: $it"); it + 1 }
    .map { println("2nd map: $it"); it + 2 }

println(resultList)
```

以下のように出力されます。

```
1st map: 1
1st map: 2
1st map: 3
2nd map: 2
2nd map: 3
2nd map: 4
[4, 5, 6]
```

この結果を見ると、**1つ目のmap関数が全要素（numberListの数値1~3）に対して適用された後、2つ目のmap関数が実行されている**のがわかるかと思います。
つまり**Listの場合、ステップごとの実行になっています。**

### Sequence: Element-by-elementの挙動

Listの例と同じロジックを以下のようなコードで実装し、実行すると…

```kotlin
//Sequence example
val numberSeq = (1..3).asSequence()
val resultSeq = numberSeq
    .map { println("1st map: $it"); it + 1 }
    .map { println("2nd map: $it"); it + 2 }
    .toList()
println(resultSeq)
```

以下のように出力されます（ちなみに、ログの最初6行は`toList()`実行時に出力されています）。

```
1st map: 1
2nd map: 2
1st map: 2
2nd map: 3
1st map: 3
2nd map: 4
[4, 5, 6]
```

この結果を見ると、**まずnumberListの最初の要素（数値1）に対して1つ目・2つ目のmap関数を順次実行し、その後が次の要素の処理へと移っていっている**のがわかるかと思います。
つまり**Sequenceの場合、要素ごとの実行になっています。**

# 違い2: Sequenceは遅延実行

遅延リストなので当然遅延実行となります。

前述の例ですと、Listの以下のコードは`.map`を呼び出すたびに`println`が実行されコンソール出力されます。

```kotlin
//List example
val numberList = (1..3).toList()
val resultList = numberList
    .map { println("1st map: $it"); it + 1 }
    .map { println("2nd map: $it"); it + 2 }
```

Sequenceの場合は最後に`toList()`を呼び出した際に`.map`内の`println`が実行されコンソール出力されます。
（＝`toList()`を呼び出すまでは`.map`をいくら呼んでも`.map`内の中身は実行されない）

```kotlin
//Sequence example
val numberSeq = (1..3).asSequence()
val resultSeq = numberSeq
    .map { println("1st map: $it"); it + 1 }
    .map { println("2nd map: $it"); it + 2 }
    .toList()
```

こういった遅延実行を開始するトリガーとなる関数は`.toList()`の他にも、`.count()`等計算をしなければ結果が生成できないような関数がそれにあたります。

# 参考

[Effective Kotlin Item 49](https://blog.kotlin-academy.com/effective-kotlin-beta-release-bbf2e0cb9c3f)
Windows10のためのX Server X410を使おう

# ヘッドレス版が無いデスクトップアプリがワークフローに必要な場合
ワークフローの実行環境の構築はDockerで実現するのが主流かと思います。
またそういった実行環境はヘッドレスのアプリで構成されることがほとんどかと思います。

ですがヘッドレス版が無いデスクトップアプリがワークフローに必要な場合もあります。
そういったアプリは画面の出し先が無いと起動に失敗し，ワークフローを実行することができません。

このようなデスクトップアプリの具体的な例として[Cytoscape](https://cytoscape.org/) Desktopがあります。このアプリは起動時にREST serverにもなりGUI操作を行わずともPythonやRからコントロールできるのですが，前述の問題があるためDockerコンテナ中に押し込むことが難しいと思われていました。
(https://github.com/cytoscape/docker-cytoscape-desktop のようにseleniumベースのイメージでxvfbとVNC serverを使う手でがんばっていた時があったんですがやはり構成が複雑で，また利用する側としても操作が難しくプロジェクトとして行き詰まっていました。)

# X410がよかった

そこで[X410](https://token2shell.com/x410/)というWindows10のためのX Serverを使ってX窓を飛ばしてみると今までの苦労が嘘のようにシンプルなDockerfileで済んだので「みんなX410(買って)使おう」というのがこの記事の趣旨です。

ちなみにそのDockerfileは https://github.com/kozo2/rcy3-docker/blob/master/Dockerfile になります。
https://github.com/cytoscape/docker-cytoscape-desktop/blob/master/Dockerfile と比べるとかなりシンプルです。


# X410をDockerといっしょに使ってみよう

例として
CytoscapeとそのRESTサービスをRから利用するためのpackage [RCy3](https://bioconductor.org/packages/release/bioc/html/RCy3.html), そしてRStudioと全部コミコミのDocker image(前述のDockerfile由来)をX410と組み合わせて使う方法を示します。
下記の順に実行します。

1. X410を起動
2. Docker for Windowsを起動
3. コマンドプロンプトなどで `docker run -e PASSWORD=hoge -p 8787:8787 kozo2/rcy3-docker` を実行
4. ウェブブラウザで`localhost:8787`を開く
5. ユーザー名 `rstudio` パスワード `hoge` でRStudio Serverにログイン
6. RStudioのTerminalタブに移動
7. Terminalタブで `export DISPLAY=docker.for.win.localhost:0.0`を実行
8. Terminalタブで `/cytoscape-unix-3.7.2/cytoscape.sh`を実行

するとWindowsデスクトップにLinux版のCytoscapeの窓が現れるかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7591/2fe88faa-01b2-65c2-7e8f-9b32e39b40f0.png)

あとはRStudioでR Markdownに書いたRCy3を活用するワークフローを実行してめでたしめでたしという感じです。

# この先は?

- Mac でも使えるようにするには?
- リモートのDocker実行環境に対して同じことをやるには?
- ワークフローのテストのためにCIでも動かすようにするには?

などいろいろこの先やりたいと思われることもあるかと思うんですが
とりあえずローカルのX410とWindowsで済むことであればこれでいいんではないかと，という感じです。

ここではネットワーク可視化ソフトを取り上げたんですが
バイオイメージインフォマティクスの分野でもX窓飛ばしが必要になるワークフローもあるんじゃないかなーと思っています。
なのでもしX410がお役に立ちそうであればぜひご購入ください。

以上。
Pythonで画像データをJSONにしてネットワーク越しに送受信する

# はじめに
　Webカメラで撮った画像をHTTPの通信を介して顔検出モデル等が動いている別のサーバーに送信するようなコードを何度か書いたので忘れないようにメモする。
　サンプルコードでは、送信側でWebカメラから撮った画像を一定間隔でサーバに送信し、受信側で顔を検出してボックスを描画した上でローカルに画像ファイルを保存している。

# 送信側

```python:sender.py
import numpy as np
import cv2
import time
import json
import base64
import requests

def send_image(img):
  # 画像を送信可能な形式に変換してJSONに格納
  _, encimg = cv2.imencode(".png", img)
  img_str = encimg.tostring()
  img_byte = base64.b64encode(img_str).decode("utf-8")
  img_json = json.dumps({'image': img_byte}).encode('utf-8')
  
  # HTTPリクエストを送信
  response = requests.post("http://localhost:8080/save", data=img_json)
  print('{0} {1}'.format(response.status_code, json.loads(response.text)["message"]))

if __name__ == '__main__':
  cap = cv2.VideoCapture(0)
  cap.set(cv2.CAP_PROP_FPS, 30)
  i = 0
  while True:
    _, img = cap.read()
    if i % 5 == 0:
      send_image(img)
    i += 1
```

# 受信側

```python:receiver.py
import os
import json
import cv2
import base64
import numpy as np
from datetime import datetime
from flask import Flask, request, Response
app = Flask(__name__)
count = 0

# 画像を保存するフォルダの作成
image_dir = "./images"
if not os.path.isdir(image_dir):
  os.mkdir(image_dir)

def detect_face(img):
  # 顔検出モデル（'haarcascade_frontalface_default.xml'）は下記リンクからダウンロードできる
  # https://github.com/opencv/opencv/tree/master/data/haarcascades
  face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
  faces = face_cascade.detectMultiScale(img, 1.3, 5)
  return faces

@app.route('/save', methods=['POST'])
def save_image():
    # データの変換処理
    data = request.data.decode('utf-8')
    data_json = json.loads(data)
    image = data_json['image']
    image_dec = base64.b64decode(image)
    data_np = np.fromstring(image_dec, dtype='uint8')
    decimg = cv2.imdecode(data_np, 1)

    # 顔検出してボックスを描画
    gray_img = cv2.cvtColor(decimg, cv2.COLOR_BGR2GRAY)
    faces = detect_face(gray_img)
    for (x,y,w,h) in faces:
      decimg = cv2.rectangle(decimg,(x,y),(x+w,y+h),(255,0,0),2)
    
    # 画像ファイルを保存
    global count
    filename = "./images/image{}.png".format(count)
    cv2.imwrite(filename, decimg)
    count += 1

    # HTTPレスポンスを送信
    return Response(response=json.dumps({"message": "{} was saved".format(filename)}), status=200)

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

# 参考
http://edosha.hatenablog.jp/entry/2017/09/05/174453
https://ysss.hateblo.jp/entry/2018/07/31/053507
【Swift】Arrayからユニークな順序付きリストへの変換を超絶シンプルに書く

# SwiftでArrayから重複なしの順序を保ったままのリストを生成したい！
と思って調べてみると、この辺の記事が参考になりますが、reduce()を使って愚直に全てをなめていく実装が散見されます:eyes:

- [swiftで配列のユニーク - Qiita](https://qiita.com/shou8/items/fb1ef9e738f637726c76)
- [配列から重複要素を削除する - Qiita](https://qiita.com/sgr-ksmt/items/157e4c6e390944237253)


今回はもっとスマートさを求めてシンプルに書けないか考えていきます:muscle:
（よりシンプルに書くことに特化しているのでパフォーマンスは気にしてません:yum:）


# Setは使える？
SwiftにはSetがあります。が、答えはノーです。
なぜなら、Setは重複しない順序を持たないリストだからです。
やってみます。

```swift
let array: [String] = ["hoge", "fuga", "hoge", "piyo", "piyo"]
// 最初に出てきた順でユニークになってほしい -> ["hoge", "fuga", "piyo"]

print(Set(array)) // "["fuga", "piyo", "hoge"]\n"
print(Set(array)) // "["piyo", "hoge", "fuga"]\n"
print(Set(array)) // "["piyo", "hoge", "fuga"]\n"
```

毎回結果が変わってしまいました。これでは順序を保つという条件が保証できません:sob:

# 順序付きのSetってないの？
Swift用はSet一択のようで、存在しないようです。
ですが、Foundation Frameworkには[NSOrderedSet](https://developer.apple.com/documentation/foundation/nsorderedset)という昔ながらのImmutableな順序付きSetのクラスが現在もサポートされています。

# NSOrderedSetを使って順序付きのユニークリストを作る
さっそく書いていきましょう。

```swift
import Foundation

extension Array where Element: Hashable {
    func unique() -> [Element] {
        return NSOrderedSet(array: self).array as! [Element]
    }
}
```

こんな感じに書けるかと思います。

どうでしょう、めちゃくちゃシンプルですよね:sparkles:
NSOrderedSetに入れて吐き出すだけです！

NSOrderedSetはObjective-C時代のものなので、インターフェースがNSArray時代のように`[Any]`となってしまいます。
そのため、入力時も`[Any]`として扱われ、出力ももちろん`[Any]`として返ってきてしまいます。

そこで、SwiftのArrayのextensionとしてメソッドを用意し、Elementの型を入出力で縛ってあげることによって、使用する側は型安全でSwiftらしく書けるようにしています。
（`as!`のような汚い実装はこうしてextensionの中に隠蔽してしまいましょう:mask:）

では、最後に結果を確認していきます。

```swift
let array: [String] = ["hoge", "fuga", "hoge", "piyo", "piyo"]

print(array.unique()) // "["hoge", "fuga", "piyo"]\n"
print(array.unique()) // "["hoge", "fuga", "piyo"]\n"
print(array.unique()) // "["hoge", "fuga", "piyo"]\n"
```

いかがでしょうか？
呼び出しもシンプルで今回の目的を達成することができました:clap:
こちらは順序をサポートしているので、何度実行しても結果は変わりません！

# P.S.
- Advent Calendarに登録しようと思っていたのにすっかり忘れてしまったので、普通の投稿です（笑）