OmniAICreator/Qiita-1.07M huggingface dataset:
HTMLと連携するアプリはUnityよりthree.jsのほうが相性がよかった話


Unity(WebGL)のオブジェクトを親のjavascriptから入力するアプリを作成していたが
https://structuralengine.github.io/FrameWebforJS
開発が進まなくって three.js に変えました


## Unity(WebGL)と親のjavascriptとの連携方法は以下の通りです。
https://docs.unity3d.com/ja/current/Manual/webgl-interactingwithbrowserscripting.html

こんな感じでjavascript と unity を連携させる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142847/f4bac061-4193-543b-033d-a059c0955ee6.png)

### Unity スクリプトから JavaScript 関数を呼び出す

プロジェクトでブラウザJavaScriptを使用する推奨方法は、JavaScriptソースをプロジェクトに追加し、それらの関数をスクリプトコードから直接呼び出すことです。そのためには、**Assets**フォルダーの「**Plugins**」サブフォルダーの下に、**.jslib**拡張子を使用してJavaScriptコードを含むファイルを配置します。プラグインファイルには、次のような構文が必要です。

```javascript:○○.jslib

mergeInto(LibraryManager.library, {

  ReceiveUnity: function (str) {
    window.ReceiveUnity(Pointer_stringify(str));
  }

});
```
次に、上記の関数を以下のように C# スクリプトから呼び出します。

```c#:○○.cs
using UnityEngine;
using System.Runtime.InteropServices;

public class NewBehaviourScript : MonoBehaviour {

  [DllImport("__Internal")]
  private static extern void ReceiveUnity(string message);

  void Start() {
      ReceiveUnity("This is a string.");
  }
}
```


### Unity スクリプト関数を JavaScript から 呼び出す

ブラウザーの JavaScript から Unity スクリプトにデータや通知の送信が必要な場合があります。推奨される方法は、コンテンツ内でゲームオブジェクトのメソッドを呼び出すことです。プロジェクトに埋め込まれた JavaScript プラグインから呼び出しを行う場合は、以下のコードを使用します。

`SendMessage(objectName, methodName, value);`

**objectName** はシーンのオブジェクトの名。 **methodName** は、現在オブジェクトにアタッチされているスクリプトのメソッド名です。**value** には文字列、数字などで、以下の例のように空にしておくことも可能です。

```
SendMessage('MyGameObject', 'MyFunction');
SendMessage('MyGameObject', 'MyFunction', 5);

SendMessage('MyGameObject', 'MyFunction', 'MyString');
```

## Unity を辞めて three.js に変えた理由

### unity は unity エディタで開発するので javascript の連携は仕様が明確になっていないと開発できない

まるで、２つの別のアプリを作っているようだった


## three.js に変えてみて

複雑な３D表現をするわけではないので three.js で十分
むしろ近年の three.js は unity に劣らないほど充実している
typescriptで書かれたnodeアプリ、クライアント サーバ どっちでコンパイルする？

# クライアント側でコンパイルして、gitにコンパイル済みjsを含むよ派

- メリット
  - サーバはpullして実行するだけだから余計なものが入らない
- デメリット
  - gitにコンパイル済みファイルが混ざる。
  - ローカルでコンパイルするのを忘れて、コンパイル済みファイルがソースコードと違う場合が発生する

# サーバ側でコンパイルするよ派

- メリット
  - gitにはソースコードのみ存在して余計なものがない
- デメリット
  - サーバにビルド環境を整えたくない
Vue.jsでselectタグの初期値にオブジェクトを設定する際の注意

# オブジェクト型（数値、文字列、論理値などプリミティブではない型）をoptionの値に設定

　以下のように、オブジェクト型を選択肢に設定して、そのデフォルト値を「v-model」で指定することができます。
　例の場合は、「selectedInfo」に「code=2」の値を設定しています。
 

```sample.vue

<template>
 <select v-model="selectedInfo"><!-- ★ v-modelに初期値オブジェクトを設定 -->
  <option v-for="info in infoList" :value="info"><!-- ★ valueにinfoというオブジェクトを設定 -->
   {{ info.label }}
  </option>
 </select>
</template>
<script>
export default {
 data() {
   return {
     //初期値
     selectedInfo: { code: '2', label: '２番' },
     //選択肢
     infoList: [
       {code: '1', label: '１番'},
       {code: '2', label: '２番'},
       {code: '3', label: '３番'},
       {code: '4', label: '４番'},      
     ]
   }
 }
}
</script>

````

# 選択値（v-modelの値）は、プロパティや値が選択肢と完全に一致していないと選択されない

 以下の例だと、remarkプロパティがないので選択されない。

```sample.vue

<template>
 <select v-model="selectedInfo"> <!-- ★ opitonに設定したオブジェクトと完全に一致しないので選択されない -->
  <option v-for="info in infoList" :value="info">
   {{ info.label }}
  </option>
 </select>
</template>
<script>
export default {
 data() {
   return {
     selectedInfo: { code: '2', label: '２番' },// ★ remarkというプロパティがない
     infoList: [
       {code: '1', label: '１番', remark: '' },
       {code: '2', label: '２番', remark: '' },
       {code: '3', label: '３番', remark: '' },
       {code: '4', label: '４番', remark: '' },      
     ]
   }
 }
}
</script>

````
code-server ローカル環境篇(3) VSCode の Plugin を 利用してみる

これは、2019年 code-server に Advent Calender の 第3日目の記事です。
2日目 の続きです。
今回も、code-server って何だろう?と言う事を解説していきます。


(1) [code-server って何?](https://qiita.com/kyorohiro/items/35bab591cd4a6b975c80)
(2) [Dockerで独自のcode-server 環境を作って見る](https://qiita.com/kyorohiro/items/d991f6fbf77a425525c5)
(3) [VSCode の Plugin を 利用してみる](https://qiita.com/kyorohiro/items/11a13d32c8748f3d7002)
(4) [DBなども含めたMVC環境を用意してみよう (1)](https://qiita.com/kyorohiro/items/4ed279dd91e39321ed20)
(5) [DBなども含めたMVC環境を用意してみよう (2)](https://qiita.com/kyorohiro/items/94c75a13ddccc5f39d85)
(6) [DBなども含めたMVC環境を用意してみよう (3)](https://qiita.com/kyorohiro/items/71a8b6ce3cbb9b36019a)
(7) [DBなども含めたMVC環境を用意してみよう (4)](https://qiita.com/kyorohiro/items/106ebb7003072a8dc989)
(8) [DBなども含めたMVC環境を用意してみよう (5)](https://qiita.com/kyorohiro/items/a019e4ab6dcda55896e0)
(9) [DBなども含めたMVC環境を用意してみよう (6)](https://qiita.com/kyorohiro/items/287364f03ed7a88f714e)
(10) [おまけ](https://qiita.com/kyorohiro/items/f96d27bba9fb23c0a097)


(NEXT->) [オンライン環境篇 1日目 作業環境を整備する](https://qiita.com/kyorohiro/items/603d6ee693fc2300079e)

(..) ローカルで、DBなどの環境も含めて構築するには
(..) オンライン上に置くには?
(..) K8Sなどの最近の流行りの環境と連携するには?
(..) Code-Serverを改造して、より良くしたい



# code-server は vscode の plugin が利用できます。

vscode の plugin を利用できます。オートコンプリート などの 補助機能やリファクタリング機能を利用して、お手軽にプログラムを書けるようになります。


python で、作成してみます。


```Dockerfile
FROM python:3.8.0-buster

RUN apt-get update
# code-server を取得するのに wget を install しておく
RUN apt-get install -y wget

# 作業ディレクトリを /works にする。どこでも良いです
WORKDIR /works

# code-server のバイナリーを取得
RUN wget https://github.com/cdr/code-server/releases/download/2.1692-vsc1.39.2/code-server2.1692-vsc1.39.2-linux-x86_64.tar.gz

# code-server を /works 配下に解凍する
RUN tar -xzf code-server2.1692-vsc1.39.2-linux-x86_64.tar.gz -C ./ --strip-components 1　

WORKDIR /works/app
ENV PYTHONPATH=/works/app

# python の plugin をインストール 
RUN /works/code-server --install-extension ms-python.python
RUN /usr/local/bin/python -m pip install -U pylint --user

# デフォルトは、/works/app で起動するようにする。
CMD [ "/works/code-server", "--allow-http", "--auth", "none", "--port", "8443", "/works/app"]



```

ubuntu に python を　インストールしても良かったのですが、公式の python image を、利用しています。

RUN /works/code-server --install-extension として、 python 向けの plugin を インストールしています。

PYTHONPATH を指定して、ルートフォルダーをしてしています。



# 試してみる

```bash
docker build -t cs03 .
docker run -v "$PWD:/works/app" -p "8443:8443" -it cs03  
```

ブラウザーを開いて、何か書く

<img width="1246" alt="Screen Shot 2019-12-03 at 5.39.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54192/811dc6cb-591b-4a71-e550-4c0e131504a1.png">


お、補完が効いていますね!!


# 次回

作成したImageを配布してみましょう。 一度、作成したImageは、ほぼほぼ、同じ状態で動かす事ができます。


# PS

## ソース
https://github.com/kyorohiro/advent-2019-code-server
PHP 7.4 から空ファイルの MIME タイプ判定が変わる

# 概要

[`mime_content_type()`](https://www.php.net/manual/ja/function.mime-content-type.php) 関数はファイルの中身を見て MIME タイプを判定する関数であるが， PHP 7.4 で空ファイル判定に関して大分類カテゴリも変わる破壊的変更が入っている。

```text:7.3
inode/x-empty
```

```text:7.4
application/x-empty
```

テストをちゃんと書いてないと地味にバグの原因になりそうなので，ここに注意喚起として共有します。

# 検証用コード

```php
<?php
var_dump(mime_content_type('data://application/octet-stream;base64,'));
```

https://3v4l.org/krp5U

データ URI スキーム，こういうときに便利。

# 独り言

最初 php.net の [アップグレードガイド](https://www.php.net/manual/en/migration74.incompatible.php) のユーザノートに投稿していたんですが，メンテナによって何故か消されるので諦めて Qiita に書くことにしました。スパムでも質問投稿でもページの話題から脱線してるわけでも無いのに一発削除はちょっと意味がわからない…

有用な投稿がよく分からない検閲に消されるのがかなり不快だったので，金輪際 php.net のユーザノートに投稿することは無いと思います。
地理空間情報の時系列管理について

技術者向けの連投ツイートを久々にしてしまいました。この辺りの知見が明確にできているのは技術者冥利につきますが、この辺りの知見を実装には移せておらず、ツイートできるレベルに留めてしまっているというのは技術者としてはちょっと悔しいですね。だけど、こうやって公開しておけば、いつか振り返っていただけることもあるかも知れない。

https://twitter.com/_hfu_/status/1201573663486697473?s=21

すなわち、次の通り。

- オブジェクトごとに発生・消滅の時点属性をつけて消去しないが模範回答だが利用者全員に高価となり機能しません。こんなこともあろうかと kakotile という仕様は考えておきました。変化は算出すれば良くなりますが、これでも高価のよう https://github.com/gsi-cyberjapan/kakotile-spec/blob/master/README.md
- 履歴確認ニーズは利用の数%以下と考え、ふつうのファイルシステムにおける Time Machine のような動きをさせるようにするとこんな感じになるものと思います。あとはタイムスタンプの運用定義を現実的なものにして確実に回す。地物が複数タイルにまたがる時には迷わず全て書き換える。
- 編集が時間的に混み合う場合にも、難しく考えずタイル単位にロックすることにすれば大抵大丈夫。ただし、タイルレベルロックを導入するより作業者の運用で作業領域が実時間の上で重ならないようにする方が経済的のはずで、ロックを実装するコストをかけられるのなら手厚いバックアップに振向けるべき。
- 手厚いバックアップというのは、実際には分散ストレージ風に実装すると、多分うまく運用すれば平常運用時のパフォーマンスも稼ぐことになる。事故の時は慎重に書き戻しにも使えることにするキャッシュみたいな感じで。これはタイルで空間分割する利点の利用でもあります。
- データのコンシステンシ確認を編集者を巻き込んだプロセスの中に埋め込むのも多分贅沢で、ライフサイクルを別にする変化確認エージェントみたいなものが別プロセスで動き問題を見つければタイル単位で通知するようにする。対話処理の速度とスケーラビリティ、ソフトウェアとデータの健全な分離のために
- ソフトウェアを更新したらデータベースの再設計とマイグレーションが生じた、が、データ事業の最大の敵です。タイル方式はソフトウェア技術の数世代、情報の1/4世代くらいの実績を積みつつあり、タイルネイティブな書き換えプロトコルは検討の価値があるはずだと思っています。
- 語り尽くしておけば、情報は書き出しと発行については安価に時点が取れることに着目すれば、タイムスタンプは実世界事象から取るのではなくあくまで編集から取ることにし、必要に応じて発行についても考慮するべき。実世界事象の時刻にこだわれるリソースがあれば、それは編集の即時化に振り向ける。
- 情報は実世界のモデルではなく観測の記録である。観測と記録すなわち編集のループを究極に短く回すことではじめて実世界のモデルに迫ることができるのであり、編集のコストを上げる方向では、実世界のモデルに迫ることは永遠にできない。抽象化すればそういうことなのかなと思います。
- ウェブの時代には、さらに刊行への考慮も欠かせません。伝わって初めて情報ですから、観測から記録、集約を経て刊行までが、要求仕様に沿った一気通貫のタイムラインのなかで考慮される必要がある。ステップごとに考えないことで得られるメリットは大きいと思っています。
- 従来通りの QA はするのだけど、細粒度にする、みたいなことになるとよい、ということですね。点検や検査、作業記録を省けという意図はまったく持っていません。念のため。
p5.jsで画像を読み込んでローカル実行したときのCORSエラーの対策

# はじめに
Processingのアプリケーションから実行するときは、サーバを勝手に立ててくれるし、p5.js公式ではWeb上でスケッチがかけるので、このエラーが出る人は、ローカルで実行したい変態くらいしかいないかもしれませんが、その対策を書きたいと思います。
私はこのエラーが出て時間を溶かしたので、皆さんはこんなことで時間を溶かさないで下さい。

# CORSエラー
CORSエラーとはp5.jsをローカルで実行していて、画像を読み込んだりするときに出るエラーです。
ブラウザのセキュリティ対策として組み込まれているもので、安全のためには仕方がないもの。
[ブラウザのセキュリティ自体をオフにしてエラーを回避する方法](https://qiita.com/ta-ke-no-bu/items/b7cd4b4719249a9c365e#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6)もあったりしますが、あまり推奨できる方法ではないように思います。
やっぱり、ローカルでサーバを立てるのが一番なようです。

# p5.js公式Wikiより
ここを見れば一発です。
[Local server · processing/p5.js Wiki](https://github.com/processing/p5.js/wiki/Local-server)
が、英語なので一応説明入れます。

# Chrome Extensionを入れる
一番簡単なのはこれです。これを使いましょう。環境設定に時間をかける必要はないです。

まず、[Web Server for Chrome](https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb/) にアクセスし、おもむろにインストール。
その後、起動するとこういう画面が出てきます。
![Web Server for Chromeの画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106956/05fa7c62-3f7a-dfb5-2b33-14400ef74f15.png)

このCHOOSE FOLDERを選択し、p5.jsのスケッチがあるフォルダを選択しましょう。
スケッチが複数あるときは、それらを含んでいる上の階層のフォルダを選択しておいても大丈夫です。

そしたら、Web ServerがSTARTEDになっていることを確認し、Web Server URLの <font color="Blue">http\://127.0.0.1:8888</font> などと書いてあるところをクリックします。
すると、ローカルサーバが立ち上がっており、スケッチを見ることができます。

# その他の方法
Python SimpleHTTPServerを使う方法やNode http-serverを使う方法がWikiには書いてありますが、正直Chrome Extensionが楽過ぎて試す気がなくなったので、そっちを使いたい方はWikiを読んで下さい。

# おわりに
楽しいp5.jsライフを！
実はWAI-ARIAで楽になるコーディングの例と、コーダーがアクセシビリティ意識を持つための1アプローチ

この記事は [Webアクセシビリティ Advent Calendar 2019](https://adventar.org/calendars/3994) の 14日目の記事です。

## はじめに

2016年で新卒で入社し、今年9月までの3年半強ほど、ニコニコ生放送のUIデザインの開発をしていました。

UIデザインの責務としては、見た目やインタラクションの他、コンポーネントのHTML定義/CSS実装までやっていました。若干フロントエンドにも入り込む、デザイナーとしては珍しいケースだったかもしれません。
(登壇記事 => [ログミー: コンポーネント指向のフロントエンド開発において、デザイナー×エンジニアの協業を通して学んだこと](https://logmi.jp/tech/articles/320378))

ニコ生フロントエンドではアクセシビリティにもある程度配慮をしてHTML定義をしており、エンジニアさん-デザイナー共同でWebアクセシビリティの勉強会や、WAI-ARIA勉強会も開かれていました。

今年9月から、ニコ生を離れ、特設ページ(いわゆるLP = ランディングページ)やポータルサイトを作る部署に移りました。

これにより、

- 大規模なプロダクト/サービス → **数ページの静的Webページ**
- UIデザイン → **グラフィック寄り/演出重視**
- React/CSS Modulesの堅牢な環境 → **ejs/scss/vanilla or jQueryの軽量な環境**
- チーム開発 → **~2人程度の少人数**
- 開発工数からリリース日を確定　→ **イベント日基準などでのリリース**

というように大きく環境が変わりました。

## 課題

UIデザインや大規模なプロダクトでは、専任のフロントエンドエンジニアもいて、コードに落とすときにアクセシビリティに配慮ができていました。
一方で、LPなどのほとんどデザイナーで完結させる業務では、見た目重視で、コーディングの際のアクセシビリティという話自体上がることも少なく、短納期のものも多いため、アクセシビリティに割く余裕もなさそうな状態でした。

## 課題解決のためにやっていること

このような状況で単に「アクセシビリティのために工数を割きましょう！」というのは現実的に難しいところなので、僕は「 **アクセシビリティに配慮することで実は実装が楽になる** プラクティスを見つけて **普及** させ、**コーディングの際にアクセシビリティを考えるきっかけ** を作る」という方向でやっていっています。

(これは生放送開発時代に、コーディングのアクセシビリティへの配慮をすることで、コードがより書きやすくなったり、エンジニアさんと意思疎通が取りやすくなったという経験から、アクセシビリティは読み上げやSEOだけでなく、マシンリーダブルにもなるため、結果として実装者にもユースフルになるのではないか、という考えに行き着いたからです。)

今回はその例をいくつか紹介したいと思います。

## aria属性によってインターフェースを統一することで実装者ごとのブレをなくす

LPは書き捨てのコードもあったりするため、実装者ごとの癖が出やすいです。これにより、引き継ぐ際に読みづらかったり、初めて書く人が迷うことがあります。
これはaria属性を使うことで、インターフェースを統一することができます。

例えば以下のような例です。

### ハンバーガーメニュー

複数ページのあるLPではハンバーガーメニューがよく使われます。

このハンバーガーメニューの実装方法は、

- class名で区別することが多いが、 単語でも `show` `open` `active` `expand` `hide` `isXXX` など、命名が非常にブレやすい
- そもそも状態をjsでしか管理しておらず、htmlに現れないこともある

のように実装者によりまちまちで、実装を引き継ぐ際や、どう書くかを毎回迷う必要があったりします。

これは、 `aria-hidden` 属性を使うことで一気にインターフェースを統一することができそうです。

### タブ切り替え

これもLPではよく出てくる要素の一つです。先ほどと同じく、実装やclass名にブレが出やすいものの一つです。

これは `aria-selected` 属性によってインターフェースを統一することができそうです。

## きっかけをもたせる

以上の２つの例も、もう少し突き詰めると、 `aria-expanded` 属性、 `tabindex` 属性、 `disabled` 属性あたりを適切に実装する必要がありますが、まずは実装のブレを無くせるという利点から `aria-hidden` や `aria-selected` の利用を普及させていきます。

これにより、まだ完璧ではありませんが、アクセシビリティを考えながらコードを書く意識を持つきっかけをつくることができます。

## `aria-hidden` 属性を覚えたら…

LPでは、装飾要素を非常によく使います。

これで困るのが、例えば背景に文字を無限に敷きたいという理由から

```html
<span class="deco">profileprofileprofileprofileprofileprofileprofile</span>
```

というかなりやばめな状況を作りがちです。

これを

```html
<span class="deco" aria-hidden="true">profileprofileprofileprofileprofileprofileprofile</span>
```

というちょっとの手間でアクセシビリティを向上させることができる、という話が自然に導入できます。

## おわりに

今回は、工数や理解を得るのが難しい状況の中でもこういうアプローチの仕方がある、という一つの例を紹介しました。

当然、WAI-ARIAだけでなく、HTMLのセマンティクスであったり、もう少し視野を広げると、色やコンテンツや文章であったりと、アクセシビリティの向上の対象はたくさんありますが、このようなコーダーにとっては身近な問題を解決するわかりやすいものがあると、アクセシビリティの意識の導入がしやすいのではないかと思います。
CODE for GIFUの活動とシビックテックのジレンマ

この記事は [CIVICTECH Advent Calendar 2019](https://qiita.com/advent-calendar/2019/civictech)の3日目です。

## CODE for GIFUの１年
今年CODE for GIFUで開催したイベントを振り返ります。
### 1月
- 定期総会を開催。今年のテーマは「代表がラクをする」に決定。

### 3月
- IODDに「あなたの望みオープンデータでかなえます」を開催。オープンデータで彼氏を探す記事を監修した西野さんに来ていただきました。
[オープンデータを利用して理想の恋人は見つかるのか!? 結婚したいOLのクリスマス直前カレシ探し 【前編】](https://sorabatake.jp/1686/)

### 4月
- メンバーで夜桜のお花見を開催。とても寒かった。

### 5月
- [Maker Faire Kyoto](https://makezine.jp/event/mfk2019/)に手芸部で出展

### 6月
- 「宇宙からあなたの望み掘り下げます」を開催。3月に実施したIODDの続きで衛星オープンデータ[Tellus](https://www.tellusxdp.com/ja/)のワークショップを世界初開催。

### 7月
- 開発合宿を高山で開催。温泉に癒される。

### 8月
- アーバンデータチャレンジのキックオフ。Code for Japanの関さん、名古屋大学の浦田さんに岐阜に来ていただく。

### 9月
- ヒーローズリーグ大垣Pythonハッカソンを開催。

### 10月
- G空間EXPO in 岐阜を共催。街歩きワークショップは台風で中止。

### 11月
- アーバンデータチャレンジ合宿を開催。Code for Mikawaの木村さんに来ていただく。もちをつく。

### 12月
- アーバンデータチャレンジの勉強会。SDGsをテーマにCode for Kanazawaの福島さんに来ていただく予定。

## シビックテックのジレンマ
今年はモヤモヤから始まりました。シビックテックという活動にジレンマを感じてきていてモヤモヤしていました。シビックテックに求められる「ともに考え、ともにつくる」「多様性」「継続性」といったキーワードは親和性が高いように見えて実は両立させようとするとジレンマが起こってしまうということです。

シビックテックオンラインアカデミー #14
 [シビックテックに感じるジレンマ](https://hackmd.io/@codeforjapan/BJ3IEJMZE/%2FkhWxAFLRQt2gYVZiOw5FjQ?type=book&fbclid=IwAR2wrBFlKafSfI_djDylRRSraXC6kQ3hjC4XMdTf-vI3SvWwj2g-cd6xxzs)

そんなモヤモヤの中、CODE for GIFUの集まりはすごくいい雰囲気だと感じていました。いい雰囲気の関係性を伸ばしたいと思い「代表がラクをする」というテーマを選びました。代表がラクをするためには必然的にメンバーにがんばってもらわなければならないからです。がんばってもらうとメンバーがCODE for GIFUに関わる理由も増えて関係性が増します。それはいい雰囲気の継続につながります。

なので、今年のCODE for GIFUの活動はメンバー主体で考えてもらいました。

あと、個人としてはメンバーのCODE for GIFUに関わってくれるモチベーションを知りたくて「さし飲み」をやりました。なかなか普段は話せない事をじっくり聴くには飲むのがよいです。自分自身の考えている事を話し、メンバーの考えていることを聴く機会はとても楽しかったです。まだすべてのメンバーと「さし飲み」できていないので来年も続けたいと思います。

来年の活動方針は既に決めていて、どんなカタチになっていくのか今から楽しみです。
（1か月前に）NRI Secure Netwars 2019に参加してきました

# Netwars2019に行ってきた感想をまとめました

myjlabアドベントカレンダー3日目です。
およそ1か月前のイベント参加記録を放出します。

## はじめに

本記事は11月9日に開催された[NRI Secure NetWars 2019](https://www.nri-secure.co.jp/event/2019/netwars)の参加記録です。

規定により具体的な問題の内容などについて記すことはできないので、オンサイトCTFに初めて参加した記録として、特にCTFに限らず「オフラインイベントへ参加するかどうか迷っている」方の背中を押すためのものとして読んでいただければ幸いです。



## プロフィール

- オンサイト（オフライン）CTF未経験、常設CTFを頑張って解く日々を送る学部3年生

- 学部2年までは企業の情報管理や体制としてのセキュリティについて専攻にしたいと考え、学んでいたが、実際に手を動かす実務的なセキュリティを学ぶ必要性を感じてCTFに手を出し始めた初心者

- その流れで[セキュリティキャンプ](https://www.ipa.go.jp/jinzai/camp/2019/zenkoku2019_index.html)に応募するなどしていた（落ちた）

- 競技プログラミングも好きではあるが、毎週決まった時間にコンテスト参加するのが苦手なのでレートは一生つかない

## 開催当日

富士ソフトアキバプラザ6Fでの開催で、開始10分〜15分前に入場したが、思ったより席が埋まっていて、どこに座ろうか小一時間迷った。

順位は気にせずとにかくなにか学んで帰ろう、という気持ちで参加したのでかなりリラックスして問題に向き合えた。

出題形式は以下の通り（[公式サイトより引用](https://www.nri-secure.co.jp/event/2019/netwars)）

>Level 1 - Played on local Linux image without root

>Level 2 - Played on local Linux image with root

>Level 3 - Attack a DMZ

>Level 4 - Pivot to intranet

>Level 5 - Master of your domain... castle versus castle

結果としてはLevel2になんとか入ったものの、その後はほとんど解けず終わってしまった。

途中、30分から40分ほど同じ問題で詰まってしまい、何度か喫煙所に行き、うーんと頭を捻っていた。ふとした弾みに恐ろしく初歩的なミスにずっとはまっていたと気づいてすこし悲しい気持ちになった。

昼食は11時ごろ、会場前方に大量の弁当が用意され、好きなタイミングで持って行って自席で食べてね、とのことだった。

朝ごはんを食べてこなかったので用意された瞬間に取りに行ったが、多くの人はずっと集中しっぱなしで、しばらくしてからぽつぽつと取りに行く人が出てくるぐらいの熱中具合だった。（少しでも多く解きたいという気持ちが先行して昼食を摂らなかったという方もいた）

イベント終了後の懇親会にはとても豪華な（重要）ケータリングが用意されていたので参加者の方々とお話ししながら頂いた。

実際に話を聞いてみるとコンピュータサイエンス以外を専攻としている方も多く、CTFやプログラミングの勉強法について盛り上がった。

懇親会ではNRI Secureの社員の方にもフランクに話しかけていただき、具体的なイメージのつかなかった仕事としてのセキュリティについても知る機会となり、モチベーションにつながった。

## 所感

とにもかくにも雰囲気を味わえたことが大きな収穫だった。ひとりで向き合ってただ考える常設CTFでは（もちろん解く分には楽しいのだが）、自分の解くスピードやその考え方の正しさについて、疑問に思うことが少なからずあった。
コンテストの途中、会場を見渡してみると、誰も彼もがひたすらにパソコンに全集中力を向けていた（当たり前といえば当たり前だけど）。悩むあまり「あれ……なんでや……」と呟いている方も。
人によってその度合いや状況は異なるが、みんな悩みながら進んでいるのだな、と感じ、ある種の安心を以て問題に集中できた。

正直このイベントへの参加を考えた際、低い順位を取ってしまうのではないか、実力が足りていないのではないかという不安が浮かんでいたが、実際に参加してからは、強くなるためには自分に足りないものを探す必要がある上、少しでも多くの情報を得ようと努めるべきだと感じた。
それに加え、意外と誰も他人の順位の低さなんて見ていないんだなと思った。（もちろん上位争いは除く）

また、競技開始前や、懇親会での講話にて「セキュリティを学ぶ者として学んだ技術を悪用しないこと」といった言葉を頻繁に聞いた。前述したとおり、CTFをやり始めたのは今年度からで、正直あまり技術的な自信や情報セキュリティを専攻するという意識が薄かったが、この言葉を聞き、自分も「セキュリティを学ぶ者」であり、それにふさわしくあるために学ばねばならないと感じた。

## おわりに

NRI Secure、SANSの方々、参加者のみなさま、参加を促してくださった先輩、同期、今回のイベントに関わってくださったすべての方々への感謝でこの記事を終わらせていただきます。本当にありがとうございました！

来年は上位目指してがんばるぞー！
Vanilla派だからjQueryは使わないがjQueryみたく書きたい


普段はプログラムを工夫することでDOM操作を最小限にしているが、それでも``querySelector()``や``createElement()``を使わざるを得ない場面がある。それを短く書けるようにしたいという話。

jQueryを使うまでもないが、jQueryの$関数みたいに短く書きたいのです。

## 単数選択

``document.querySelector()``と同じ

```js:単数選択
$('セレクタ') // 戻り値はDOM要素
```


## 複数選択

セレクタの頭に``*``を付けると複数選択。``document.querySelectorAll()``と同じ

```js:複数選択
$('*セレクタ') // 戻り値は配列
```

※NodeListが返ってきて嬉しかったことがないので、戻り値は配列にした。


## HTML文字列のDOM化
この関数はタグ付きテンプレートリテラルとして使うと、その文字列をDOM化します。

```js:HTML文字列のDOM化
$`HTML文字列` // 戻り値はDOM要素
```
戻り値はDOM要素。
トップレベルに複数の要素がある場合は``DocumentFragment``となります。


## ソースコード

ご自由にお使いください。

```js
function $(selector, ...vars){
    if(Array.isArray(selector)){
        const template     = document.createElement('template')
        template.innerHTML = selector[0] + vars.map((v,i) => v + selector[i+1]).join('')
        const content      = template.content
        return content.childNodes.length === 1 ? content.firstChild : content
    }
    else if(selector.startsWith('*')){
        return Array.from(document.querySelectorAll(selector.slice(1) || '*'))
    }
    else{
        return document.querySelector(selector)
    }
}
```
三井住友信託銀行プログラミングコンテスト2019に参加してみた

#感想
AtCoderのコンテスト初参加。結構疲れた。てかムズイ。普通に数学的な発想が求められるんだな〜。
解ければ快感、解けなければうつ病。
https://atcoder.jp/contests/sumitrust2019
以下自分の回答
#A November 30
```ruby
M1,D1 = gets.chomp.split.map(&:to_i)
M2,D2 = gets.chomp.split.map(&:to_i)

ret = 0

if M1 == 12
  if D1 == 31
    ret = 1
  end
end

if M1 + 1 == M2 
  ret = 1
end

puts ret
```
D2が１かどうか判定すれば良かっただけなのに、なにしてるんだろうか。ぐぬぬ。

#B Tax Rate
```ruby
N = gets.to_i

X = ":("

a = N * 100 / 108
if ( N * 100 ) % 108 != 0
  a += 1
end

if a * 108 / 100 == N
  X = a
end
puts X
```
結構迷ってしまった。てか正規のやり方なのか怪しい。
Xに文字列いれた上に数値入れてグロい。Ruby凄いな。
#C 100 to 105
```ruby
X = gets.to_i

N = X / 100

can = 0

for i in 0..N
  x = X - i * 100
  if 0 <= x && x <= 5 * i
    can = 1
    break
  end
end

puts can
```
特にコメントなし（実際に解いてる時は一瞬で解けた嬉しさで内心ウッキウキだった）
#D Lucky PIN
```ruby
N = gets.to_i
S = gets.chomp

count = 0
for i in 0..9
  a = S.index(i.to_s)
  if a != nil
    for j in 0..9
      b = S.index(j.to_s, a+1)
      if b != nil
        for k in 0..9
          c = S.index(k.to_s, b+1)
          if c != nil
            count += 1
          end
        end
      end
    end
  end
end

puts count

```
最初３００００個を３重ループして時間オーバーになった（あたりまえやん）
ここまでは100分以内にできた。
#E Colorful Hats 2
ここから後日解き直した。

```ruby
N = gets.to_i
A = gets.split.map &:to_i

t = [ 0, 0, 0 ]

result = 1
for i in 0..N-1
  if t.count( A[i] ) == 0
    result = 0
    break
  end
  result = ( result * t.count( A[i] ) ) % 1000000007
  for j in 0..2
    if t[j] == A[i]
      t[j] += 1
      break
    end
  end
end
puts result

```
これみんな簡単って言ってるのにわからなくて泣いた。
それぞれの帽子の数を保持して、その値と一致した分だけ場合分けができるという発想が無かった。
ブログラミングじゃなくても解けなかったと思う。悲しい。理系とは何だったのか。
#F Interval Running
```ruby
T = gets.split.map(&:to_i)
A = gets.split.map(&:to_i)
B = gets.split.map(&:to_i)

b = ( T[0] * A[0] + T[1] * A[1] ) - ( T[0] * B[0] + T[1] * B[1] )
if b == 0
  puts "infinity"
else
  a = T[0] * ( A[0] - B[0] )
  if a * b > 0
    puts 0
  else
    c = ( a / b ) * -2
    if a % b != 0
      c -= 1
    end
    puts c
  end
end
```
E問題で精神がやられて変数名とか見づらいしif文もおかしいけど一応解けた。
実際のコーディングだったら絶対に修正する。
A問題では変数に1つずつ入れているのにF問題では配列にいれる謎。（表記揺れってやつになるのかな）
自分で書いといてなんだけど```*-2```とかいう箇所気持ち悪すぎる。
横軸時間、縦軸距離のグラフを書くのがわかりやすかった。
VimをVisual Studio Codeっぽく使ってみる ①

[Vim2 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/vim2) の 3日目 担当の @Chitama です。
Webやアプリの開発をやってます。

[Visual Studio Code](https://azure.microsoft.com/ja-jp/products/visual-studio-code/) を立ち上げずに `Vim`でコードを書いてみたらと、いつもよりメモリの使用量が<font color="blue"> 1GB </font>ほど少なかったので、`Vim`だけで`Visual Studio Code`っぽく使えないか試してみました。
今回は、vim標準のファイラ`netrw`だけでできる範囲でやってみます。

## 標準設定で使ったとき
まず、まっさらな `vimrc` だとこんな感じです。

### プロジェクトフォルダを開く

- 基本操作
  - `edit <開きたいプロジェクトフォルダパス>`
  - `e <開きたいプロジェクトフォルダパス>`

<img width="500px" alt="trends" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91552/6e1c54ad-5583-5096-af38-6a0e16f4a837.png">

- やりたいこと
  - 開いた時に上部に表示される情報を非表示にしたい
  - ツリー形式で展開するように表示したい

### ファイルを開く

- 基本操作
  - ディレクトリ/ファイルを開く: `Enter`
  - 水平方向で開く（画面分割）: `o`
  - 垂直方向で開く（画面分割）: `v`
  - ファイルを閉じる： `:bd`
  - 右のウィンドに移動: `<Ctrl+w> l`
  - 左のウィンドに移動: `<Ctrl+w> h`
  - ウィンドに移動: `<Ctrl+w> 長押し`

<img width="500px" alt="trends" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91552/ff7f69a0-3fe7-5b1c-ecd0-82f0e8191b85.png">

- やりたいこと
  - ファイルが左側に開くので、右側にしたい
  - 行番号を表示したい
  - コードに色分けしたい

## vimrcを追記
やりたいことが実現できるように `vimrc` を書いてみます。

```vim

" プラグインを有効にする
filetype plugin on

"##### nwtrw #####
" 上部の情報を非表示にする
let g:netrw_banner = 0
" 表示形式をTreeViewに変更する
let g:netrw_liststyle = 3
"コードの色分け
syntax on

"#####表示設定#####
"行番号を表示する
set number
" 新しいウィンドウを下に開く
set splitbelow
" 新しいウィンドウを右に開く
set splitright
```

- Vim（左）、 Visual Studio Code（右）

<img width="395x" alt="trends" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91552/76b78b7a-e5e5-802c-6c6a-db320ecfd09f.png"> &nbsp;&nbsp;&nbsp; <img width="385px" alt="trends" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91552/8dc23424-4fbb-6313-c3a1-5dcfee1b9b16.png">


# とりあえず
見た目だけっぽくしてみました。
次は、開発で使ういろいろな機能も試してみようと思います。
Open Street Mapの全球データをS3に持ってくる

# 目的

AWSのデータ処理の便利サービスを使って、Open Street Mapの全地球のデータを処理したい。そのためには、まず全球データをダウンロードしなきゃいけない。
でも、いったん自分のローカルPCに落としてそこからS3にあげると、ネットワーク帯域を余計に使うよね。
なるべくダイレクトにS3に持っていけない？

# Planet OSM

[Planet OSM](https://planet.openstreetmap.org/)
Open Street Mapのすべてのデータをひとまとめにしたもの。要は、全球データ。PBFというバイナリフォーマットで、48GB（2019年12月3日現在）ある。現代ではビッグデータというほど大きくないかもしれないが、やはり扱うのが大変なレベルのデータ量。
（むしろ、地球全体のデータを **”たった”** 50GBに収められるの？という感想もある）
地球全体のデータを作り、世界のどこでもサービスを提供するためには、避けては通れないデータ。

# 方法

AWSアカウントがあるのは前提。まず、Amazon LinuxのEC2インスタンスを立ち上げる。ここで、EC2のディスクにいったんダウンロードして、そこからさらにS3にアップロードするのはしたくないので、EC2は適当な小規模なインスタンスで、ディスクも極小にしておく。

インスタンスにログインして、aptなどでcurlとaws-cliが使えるようにして、おもむろに以下のコマンドをたたき、OSM全球データを落としてきたのを、そのままパイプでS3のバケットに投入する。

ダウンロード元のURLは適宜確認すること。自分に近いミラーを使うと早いが、オリジンは可能な限り避けた方がいいと思う。アップする先のバケット名やファイル名も、適宜自分の用途に従い変更する。また、バケットの権限やセキュリティの設定も適宜行うこと。

```
curl https://ftpmirror.your.org/pub/openstreetmap/pbf/planet-latest.osm.pbf | aws s3 cp - s3://osm-planet-data/planet-latest.osm.pbf
```

しばらく（自分でやったときは、数時間のオーダー待った）待てば終わるはず。
これでEC2のディスクを汚さずに直接S3に放り込むことができる。
終わったらインスタンスは消してしまおう。
更新されるごとにこれやるのもきついので、差分データをうまく使うことを考えたい。

# それだけ？

今日はこれだけ。
リンクスリングスにおけるPubSubサービス導入事例

本記事は、[サムザップ Advent Calendar 2019 #2](https://qiita.com/advent-calendar/2019/sumzap2) の12/8の記事です。

# はじめに
本記事では、サムザップが提供するマルチ陣取りバトル「[リンクスリングス](https://linqsrings.jp/)」で導入したPub/Subサービス「[Pusher](https://pusher.com/)」の紹介と導入事例を記載します。

# 主な用途
まずはじめに、ゲーム内の用途として以下のようなものが予定されていました。

- チャット
- ミッション達成通知
- メンテナンス通知
- リアルタイムなマッチング

etc.

# サービスの選定
これらを実現するにあたり、様々なサービスを検討・検証しました。

- Google Cloud Pub/Sub
- PubNub
- AmazonSNS
- AmazonIoT
- RealtimeFramework

etc.

開発中は、RealtimeFrameworkを利用していたのですが、問い合わせのレスポンスが悪かったことや、ホームページの証明書の期限が切れていた時期等があり、運用で利用するにはリスクが高いと判断し、開発途中で**乗り換え先未定のまま**乗り換えることだけを決めました。

そこから急ピッチで選定を開始し、以下のような選定基準でPusherを選択することになりました。

- 料金
- リアルタイム性
- スケーラビリティ
- サービス信頼性
- 実装難易度

Pusher以外のほとんどのサービスは、料金もしくはリアルタイム性において今回の要件を満たさなかったため、不採用となりました。

# Pusherとは
ここでPusher…というか、PubSubについてもう少し説明したいと思います。

> 非同期メッセージングパラダイムの一種であり、メッセージの送信者（出版側）が特定の受信者（購読側）を想定せずにメッセージを送るようプログラムされたものである。
> *引用元：ウィキペディア*

以下の図のように、送信者(publisher)は、チャンネルに対してメッセージを送信するだけで、受信者(subscriber)を知る必要がなく、受信者は送信者を知らずとも、必要なチャンネルを購読することで、必要なメッセージを受信する仕組みです。

![109D3E0C-3720-4167-9AB5-36BF9BA5825D.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209922/eb68b0a4-f29f-297c-f474-bd785cb444e7.png)

Pusherは、このPubSubを簡単に実現し、かつスケーラブルでリアルタイムなサービスとなっています。

# 基本的なシーケンス
以下Pusherのドキュメントより引用させていただきました。
かなりシンプルなシーケンスで、クライアント/サーバー共にSDKが用意されているのもあって、導入も容易でした。
サンプルのjavascriptがあったり、ダッシュボードにデバッグ用のツールがあったりするので、サーバ側だけで動作確認することも出来ました。

![E0ADD193-62CB-4E50-A78F-F8EFE8C4B454.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209922/5e38f6e3-3e2d-972b-e62b-3147a9d3a46c.png)

# その他

- webhook
  - ユーザーがチャネルに入ったり、抜けたりしたイベントを通知してくれる機能があります。
  - リンクスリングスでは、マッチングの入退場処理に利用していました。
  - 注意点として、追加・削除のイベントはそれぞれエンドポイントを設定することが出来ず、1つのエンドポイントに両方のイベントが飛んでくるので、エンドポイント内の処理でイベント切り分けが必要です。
  - またイベントに対して、Httpステータスとして200を返すまでリトライが来るのも注意ポイントです。
- 従量課金
  - メッセージ数（1メッセージあたりのサイズ上限あり)による従量課金のため、メッセージ数が多くなり過ぎないように見積りが必要です。
  - 送信自体もメッセージ数にカウントされるので注意が必要です。
  - とはいえ、他のサービスに比べると格安でしたし、開発中は無料版で十分でした。
- 海外のサービスなので、契約する場合は先方と英語でやり取りする必要があります。
  - 最初メールを送ったら、「電話で話せるか」と返信があって焦りました。。
  - ただそれくらい問い合わせのレスポンスもよく、サービスも安定していました。

# 最後に
Pusherはお手軽に使えて、リアルタイム性も良く、スケーラブルで、営業の方も色々柔軟に対応してくれる(サービスを東京リージョンに作ってくれました)、良いサービスなので、PubSubサービスの選択肢の1つとして頭の片隅に持っておくと良いかもしれません。

それでは、快適な開発ライフを〜。

明日は @kotaroy  さんの記事です。
何度も使う中間テーブルに名前をつけてクエリを見やすくする

# 一人アドカレ

| 日 | ポスト |
|---:|:------|
|  1 | [一時テーブルが存在していたら削除する](https://qiita.com/sotoattanito/items/10e65399caf34fae3017) |
|  2 | [何度も使う中間テーブルに名前をつけてクエリを見やすくする](https://qiita.com/sotoattanito/items/673fb0ceb543fdd391e2) |
|  3 |       |
|  4 |       |
|  5 |       |
|  6 |       |
|  7 |       |
|  8 |       |
|  9 |       |
| 10 |       |
| 11 |       |
| 12 |       |
| 13 |       |
| 14 |       |
| 15 |       |
| 16 |       |
| 17 |       |
| 18 |       |
| 19 |       |
| 20 |       |
| 21 |       |
| 22 |       |
| 23 |       |
| 24 |       |
| 25 |       |

# 何度も使う中間テーブルに名前をつけてクエリを見やすくする

この記事は自身のブログを書き直したものです。

## 課題
クエリを書いていると、何度も同じ中間テーブルを書いている時がある。自分の場合、テーブル結合を幾つかしている時に多い。
同じ中間テーブルのクエリを何度も書いているとミスの元だし、修正するにも手間だし見難いしで大変なので、**共通テーブル式**を使い見やすくする。


## 準備
### 実行環境
 * SQL Server 2017 Standard
   * 2008 R2 Express, 2012 Standardでも確認済み
 * Management Studio 2017

### サンプルデータ
fruitsテーブル

|ID|Name|Price|
|:--:|:----:|:-----:|
|1|apple|100|
|2|apple|120|
|3|orange|50|
|4|orange|40|
|5|apple|80|
|6|orange|60|
|7|banana|120|
|8|banana|150|

## 解法
共通テーブル式とは名前をつけた中間テーブルのこと。WITH句を使うことで実現できる。

```sql
WITH [commonTableName] AS (
     SELECT [hoge] AS [column1]
          , [fuga] AS [column2]
       FROM [someTbl]
      WHERE [someCol] = 'piyo' )
SELECT [column1]
     , [column2]
  FROM [commonTableName]
```

WITH句からはじまり、WITHの後には共通テーブルの名前[commonTableName]が続く。
その後の括弧内のSELECT文は共通テーブルの定義になり、このSELECT文の結果が共通テーブルとなる。
括弧の後のSELECT文から共通テーブルが使用可能となる。
共通テーブルの特徴として、自己参照が可能で再帰クエリが書ける点がある。

fruitsサンプルテーブルを使った共通テーブルの例が以下。
共通テーブル式に果物ごとの最安値を抽出する文を指定して、SELECT文で各レコードごとに一致する果物の値段と最安値の差額を抽出した。

```sql
WITH [commonTable] AS (
     SELECT [Name]
          , MIN( [Price] ) AS [minPrice]
       FROM [fruits]
      GROUP BY [Name] )
SELECT [fruits].[ID]
     , [fruits].[Name]
     , [fruits].[Price] - [commonTable].[minPrice] AS [diffPrice]
  FROM [fruits]
 INNER JOIN [commonTable]
         ON [commonTable].[Name] = [fruits].[Name]
 ORDER BY [ID];
```

| ID | Name | diffPrice |
|---:|:-----|----------:|
|1|apple|20|
|2|apple|40|
|3|orange|10|
|4|orange|0|
|5|apple|0|
|6|orange|20|
|7|banana|30|
|8|banana|60|
|9|cherry|0|
|10|cherry|100|
|11|pineapple|0|
|12|pineapple|30|
|13|banana|0|

# 感想
この例だとぜんぜん共通テーブル式の便利なところが伝わらないかな。。。
同じクエリを何回も書くような時使うと、すっきりして見やすいクエリになります。
再帰クエリも便利だし記録しておきたい。

共通テーブルは中間テーブルである、というと一時テーブルとの違いがわからないので別途記事を書きたい。
⇒『[共通テーブル式と一時テーブルの参照可能な範囲の違い](https://qiita.com/sotoattanito/items/9fa81e9acc9af915600f)』を書きました。

# 参考

 * [共通テーブル式の使用](https://technet.microsoft.com/ja-jp/library/ms190766(v=sql.105).aspx)
Salesforceのレコードへの共有設定（共有モデル）をイメージで説明~初心者向け~

#大切なのはイメージをもつこと
ハロー、エブリワン。
試験管生まれ、研究所育ちの遺伝子操作系Adminのきたづめです。
今日もDNAが疼く。

この素晴らしい記事は [Salesforce Advent Calendar 2019](https://qiita.com/advent-calendar/2019/salesforce) の4日目の記事となります。

今回はSalesforceで混乱しがちなレコードへのアクセス権限がよくわからないといった初心者の方に基本概念を掴んでもらうための記事となっております。

**◎網羅する範囲**

- オブジェクト権限
- 組織の共有設定
- ロール階層
- 共有ルール
- 手動共有
- フルアクセス権限




**◎詳しく知りたい人は公式情報をチェック:heartbeat:**
[Salesforce公式ヘルプ（共有設定）](https://help.salesforce.com/articleView?id=managing_the_sharing_model.htm&type=5)
[Trailhead（データセキュリティ）](https://trailhead.salesforce.com/ja/content/learn/modules/data_security)

#イメージ説明

##全体イメージ
![名称未設定.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532994/dab38c2a-7c7b-ecd7-634f-0b992341a147.jpeg)

![名称未設定.002.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532994/3d5f975f-4e78-f489-6604-62552b55f3bb.jpeg)

![名称未設定.003.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532994/01dcd538-220c-38ab-8fbf-5d616384ad86.jpeg)

##書庫や書類の取り扱いについてのイメージ
![名称未設定.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532994/bb1d5374-65d6-b2a7-f3ab-3b3885cd2fe6.jpeg)

##他人の書類の取り扱いについての大まかな設定イメージ
![名称未設定.005.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532994/c53a242c-22d4-21a4-3df2-a2355df0cec4.jpeg)
![名称未設定.006.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532994/0a95afd5-58e5-3af0-db69-ecd848dca126.jpeg)


##他人の書類を見せる条件の設定イメージ
![名称未設定.007.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532994/6ebd22bf-7db6-4da3-64bb-94801d888f95.jpeg)
![名称未設定.008.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532994/4aa200be-aaff-c56a-b7a7-c898bf3b484d.jpeg)
![名称未設定.009.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532994/e62d3a5e-d73a-bbd8-ca5f-3a61422f2b75.jpeg)


##フルアクセスのイメージ
![名称未設定.010.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532994/43afe39e-4116-9f3b-5051-61522e21cfd0.jpeg)
React-Redux 初歩　boolean

Redux初心者のメモ。
ログイン状態を判定したい時にboolean型のstateをReduxで管理してみる。（ここではログイン処理やフォームの実装はしません。）

# Action
ストアの state を変更するためのメッセージを定義。

```ruby:src/actions/index.js
export const SIGN_IN = 'SIGN_IN'
export const SIGN_OUT = 'SIGN_OUT'

  export const sign_in = () => {
    return {
      type: SIGN_IN
    }
  }

  export const sign_out = () => {
    return {
      type: SIGN_OUT
    }
  }
```

# Store

Reducerをもとに、storeを作成

```ruby:src/index.js

import React from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'

import reducer from './reducers'
import App from "./components/App";

const store = createStore(reducer)


ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);


```

# Reducer
actionを受けてstateを変更する.

```ruby:src/reducers/islogged.js
import { SIGN_IN ,SIGN_OUT} from '../actions'

//初期値にfalse
const initialState = { value: false }

export default (state = initialState, action) => {
  switch (action.type) {
    case SIGN_IN:
      return { value: true  }
    case SIGN_OUT:
      return { value: false }
    default:
      return state
  }
}
```
Reducer群を管理する。Reducerが増えた時に必要。

```ruby:qiita.rb
import { combineReducers } from 'redux'
import islogged from './islogged'

export default combineReducers(({islogged }))
```

# コンポーネント

storeで呼び出したAppコンポーネント
Stringメソッドでbooleanを文字を変更して表示している。
buttonを押すとstateが切り替わる。

```ruby:src/components/App.js
import React, { Component } from 'react'
import { connect } from 'react-redux'

import { sign_in,sign_out } from '../actions'

class App extends Component {
  render() {
    const props = this.props
    return (
      <React.Fragment>
        <div>count:{String(props.value)}</div>
        <button onClick={props.sign_in}>+</button>
        <button onClick={props.sign_out}>-</button>
      </React.Fragment>
    );
  }
}


const mapStateToProps = state => ({ value: state.islogged.value  })


const mapDispatchToProps = dispatch => ({
  sign_in: () => dispatch(sign_in()),
  sign_out: () => dispatch(sign_out())

})


export default connect(mapStateToProps, mapDispatchToProps)(App)
```
Twistのchatbotを拡張してみた

昨日作ったchatbotがあのままだと全く実用性がないので、もうちょっとサンプルっぽくしました

昨日作ったもの
https://qiita.com/usk81/items/bfd15ec3c5ecc23d0b8f

Twistに関しては昨日作ったものの記事を参考にしてください


# 成果物

コメントしたものをそのまま返すようにしました

<img width="743" alt="スクリーンショット 2019-12-02 23.46.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9094/0fa046a8-63f8-5072-d518-9aba8b110eee.png">

Page: https://github.com/usk81/twistbot
Release: https://github.com/usk81/twistbot/releases/tag/v0.0.1-alpha2

## Request Body

Request Bodyは`x-www-form-urlencoded`で送られているようです。
どうせなので、Structureにおこしてみました。
というか、Developerページと差異があってほぼここの解析に時間使ってました。

Structureへのデコードは`Gorilla`の`Schema`を使用しました。
https://github.com/gorilla/schema

今回は対応しませんでしたが、リクエストの中に`url_ttl` と `url_callback` というものがあります。
`url_ttl`にはタイムスタンプ
`url_callback`にはURLが格納されています。
リクエストの返却に時間がかかる場合は、一度202で空リクエストを返して、`url_ttl`で設定されている制限時間内に`url_callback`にリクエストを返せばいいそうです。

```go
type TwistOutgoingRequest struct {
	EventType         string `schema:"event_type"`
	WorkspaceID       int    `schema:"workspace_id"`
	Content           string `schema:"content"`
	UserID            int    `schema:"user_id"`
	UserName          string `schema:"user_name"`
	URLCallback       string `schema:"url_callback"`
	URLTTL            int    `schema:"url_ttl"`
	MessageID         int    `schema:"message_id"`
	ThreadID          int    `schema:"thread_id"`
	ThreadTitle       string `schema:"thread_title"`
	ChannelID         int    `schema:"channel_id"`
	ChannelName       string `schema:"channel_name"`
	CommentID         int    `schema:"comment_id"`
	ConversationID    int    `schema:"conversation_id"`
	ConversationTitle string `schema:"conversation_title"`
	VerifyToken       string `schema:"verify_token"`
}
```

## Handler

### 修正前

```go
func botHandler(w http.ResponseWriter, r *http.Request) {
	if r == nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("can not get request"))
	}
	defer r.Body.Close()
	bs, _ := ioutil.ReadAll(r.Body)
	w.Write([]byte(fmt.Sprintf("header: %s, body: %s", r.Header.Get("Content-Type"), string(bs))))
}
```

### 修正後

デバッグも兼ねて、ログも仕込んでみました。
サーバのログをミドルウェア層をzapで作ったので、ソフトウェア層も揃えました。
https://github.com/uber-go/zap

message_idとかが返ってきてたので、APIでmessage取得しないといけないのかと思ってましたが、contentに平文で入ってました。

```go
func botHandler(w http.ResponseWriter, r *http.Request) {
	lg, _ := zap.NewProduction()
	defer lg.Sync()

	if r == nil {
		lg.Error("can not get request")
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("can not get request"))
	}
	if err := r.ParseForm(); err != nil {
		lg.Warn(fmt.Sprintf("failed to parse request %s", err.Error()))
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("request is invalid"))
	}
	var req TwistOutgoingRequest
	if err := decoder.Decode(&req, r.PostForm); err != nil {
		lg.Error(fmt.Sprintf("fail to decode parsed request %s : %#v", err.Error(), r.PostForm))
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
	}
	w.Write([]byte(req.Content))
}
```
QGISで高さを持った建物ポリゴンを作る

# 目的

3D表示のための高さ情報を持った建物の形状ポリゴンのデータをQGISを使って作ります。
元にするデータは、12/1のところでダウンロードしてきた国土地理院の基盤地図情報と、JAXAの公開している全球数値地表モデルです。

# 背景

日本全国のデータとしての建物高さを3D表示するために使える生データで、さらに個人が無料で使えるものは、私が知る限り存在しません。
（もし日本全国を同条件でカバーしているGISなどで自分で操作可能な無料の建物高さデータを知っている人がいたら教えてください。お願いします。）
Open Street Mapなどでは、”一部”の建物の高さ情報が記載されていますし、Google Mapでは3Dで表示できるので、それデータあるよねと言われればあります。また、ゼンリンさんなどが各種有料地図データを販売しています。
つまり、データとしてはあるが、個人では無料で自由に使えないというのはそういうことです。
そこで、公開されているデータを組み合わせて、それなりの根拠のある形での欲しいデータを作ってしまおうというのが、本稿の本題です。

# 早速やってみる

QGISは、3.8.2を使っています。が、基本的なことばかりなので前後の近いバージョンであればそんなに問題はないかと思います。最近のQGISはバージョンアップが速くて、ちょっと追いつけてない感があります。

## QGISに読み込み

前回作ったShapeファイルをQGISに読み込みます。
メニューの「プロジェクト」-「新規作成」などでプロジェクトを作成し、「プロジェクト」-「保存」で任意の場所に保存しておきます。
メニューの「レイヤ」-「レイヤの追加」-「ベクタレイヤの追加」で開くダイアログで、前回「基盤地図ビューア」で変換したShapeファイルを読み込みます。建物外郭線データと標高データを読み込みます。
同様に、メニューの「レイヤ」-「レイヤの追加」-「ラスタレイヤの追加」で開くダイアログから、全球数値地表モデルのTiffを読み込みます。
QGIS画面上に次のような地図表示がされました。

![20191202_fig_qgis.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30257/6a330ea6-4a89-f2c5-68cd-111154cb47a3.png)

## 標高データ事前加工

標高データに全球数値地表モデルの高さ情報を格納するカラムを追加します。
標準のレイアウトでは左下にあるレイヤパネルで、読み込んだ標高データのレイヤを選択し、右クリックメニューから、「プロパティ」を選択します。
表示されたダイアログ上の「ソースフィールド」タブから、鉛筆マークの編集モード切替をクリックし、新規フィールドアイコンをクリックして、高さ情報格納のためのフィールドを作成します。
名前はDSMなどとして、タイプにDoubleを選択、長さと精度は6,2あたりを入れておきましょう。
保存してダイアログを閉じます。

## 標高データに全球数値地表モデルデータを結合

次に、メニューの「プロセッシング」-「ツールボックス」でツールボックスを表示して、検索ボックスに「v.what.rast」と入力して出てきた、「v.what.rast」という項目をダブルクリックしてツールを起動します。
パラメータとして点ベクタに標高データ、入力ラスタに全球数値地表モデル、入力ベクタはpoint、更新する属性名に先ほど作ったDSMを指定して、実行します。
これで、DSMフィールドに、ポイント直下の全球数値地表モデルの高さデータが格納されました。

## 高さの計算

フィールド計算機を使います。
アイコンバーの中のそろばんのアイコンをクリックします。でてきたダイアログで、DSM-DEM の計算を実行します。
「新しいフィールドを作る」にチェックを入れ、出力フィールド名に「height」、出力フィールドタイプを「小数点付き数値」にします。出力フィールド長は、6、精度を 4 くらいにしておきましょう。「フィールドと値」の DSM をダブルクリックし、式テキストフォーム
に入れます。演算子ボタンの「-」をクリックし、標高をダブルクリックして式「"dem" - "標高"」を作ります。最後に、「OK」ボタンを押して実行します。これで、全球数値地表モデルから標高を引いた値が計算され新規フィールドに保存されます。

標高データは、地表面の標高のデータです。数値地表モデルは、地表、つまり、建物や植生などを含んだ高さのデータです。そのため、引き算すると地表にあるものの高さ＝建物の高さなどになります。
ただ、今回使ったデータは、数値地表モデルが30m精度なので、かなり荒いデータです。そのため、かなりざっくりとした高さデータになっています。

## 建物外郭線データに結合

次に、建物外周線ポリゴンのデータに、地物の高さの値のポリゴン内での最大値を取り込んでみましょう。
ツールボックスペインで、「v.vect.stats」を検索します。見つかったらダブルクリックしてツールを起動します。設定する項目は、「Name of existing vector map with points」に、地物の高さまで計算した標高レイヤ、「Name of existing vector map with areas」に、建物の外周線データのレイヤを、「Method for aggregate statistics」には、領域内に複数の点が含まれる際に集約する時の計算を指定しますが、maximum を設定します。
「Column name of points map to use for statistics」には、地物の高さが入っているフィールドの名前を指定します。「Column name to upload points count」には、領域に含まれる点数を格納するフィールド名、「Column name to upload statistics」には、集約した結果の値（この場合は、領域に含まれる点の height の最大値）を格納するフィールド名を指定します。最後に、「Updated」の入力フィールドに、「...」ボタンから、「Saveto File...」を選んで、保存するファイル名を指定し、「実行」ボタンを押してください。

これで、二次元の平面だった建物データに、一応の裏付けのある高さデータを取り込むことができました。

# さいごに

仕事でやるなら、お金を出してデータを買うのが妥当ですが、個人でやる場合どうしてもお金が大きな制約になりがちです。特に地図データはかなり高価なので、このように様々な工夫で乗り越えていくことになります。
とはいえ、生のデータを自分で加工して、欲しい情報を取り出せるというのは、逆にできることの可能性が広がるのではないかとも思います。
次は、このデータをUnityに取り込んで3Dで表示してみたいと思います。

※ちなみに、ここまでのお話のもっと詳細が、[こちら](http://klabgames.tech.blog.jp.klab.com/archives/tbf07.html)で配布している、KLab Tech Book Vol.2に載っています。よろしければそちらもご覧ください。
KuberneteisでPodのホスト名を指定する方法

Windows 10
Docker Desktop 2.1.0.5
　Engine: 19.03.5
　Compose: 1.24.1
　Kuberneteis: v1.14.8

hostname:のうしろに指定したいホスト名を指定してデプロイ

```nginx.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      hostname: test
      containers:
        - name: nginx
          image: nginx:1.16.1
          ports:
            - containerPort: 80
          resources:
            limits:
              cpu: "100m"
              memory: "64Mi"
            requests:
              cpu: "100m"
              memory: "64Mi"
```

```
kubectl apply -f nginx.yaml
 kubectl get po
NAME                     READY   STATUS    RESTARTS   AGE
nginx-7bff648d58-cm58p   1/1     Running   0          9s

kubectl exec nginx-7bff648d58-cm58p hostname
test
```
MySQLのスロークエリのログを出力する設定


## 前提環境
- MySQL 8.0

## スロークエリログを出力する設定

```my.cnf
slow_query_log
long_query_time=1
log_queries_not_using_indexes
```

- **slow_query_log** … スロークエリログ出力を有効にする
- **long_query_time** … 指定した秒以上かかったクエリ（デフォルトは10)
- **log_queries_not_using_indexes** … インデックスを利用していないクエリを出力する

### 反映確認
※hostname … サーバに設定されているホスト名

```
mysql > SHOW VARIABLES LIKE 'slow_query%';
+---------------------+------------------------------------+
| Variable_name       | Value                              |
+---------------------+------------------------------------+
| slow_query_log_file | /var/lib/mysql/[hostname]-slow.log |
+---------------------+------------------------------------+
1 row in set (0.01 sec)
```

## スロークエリログ出力確認
※hostname … サーバに設定されているホスト名

```bash
cat /var/lib/mysql/[hostname]-slow.log
/usr/sbin/mysqld, Version: 8.0.16 (MySQL Community Server - GPL). started with:
Tcp port: 0  Unix socket: /var/lib/mysql/mysql.sock
Time                 Id Command    Argument
# Time: 2019-12-01T17:09:57.232128Z
# User@Host: root[root] @ localhost []  Id:     8
# Query_time: 0.002923  Lock_time: 0.000386 Rows_sent: 2  Rows_examined: 575
SET timestamp=1575220197;
SELECT * FROM performance_schema.variables_info WHERE VARIABLE_NAME LIKE 'slow_query%';
```

Query_time … クエリの実行にかかった時間
Lock_time … ロックした時間
Rows_sent … クエリ実行後に返却した行の数
rows_examined … クエリ実行で読み込んだ行数
jQuery 基本その2 フォームの情報取得　チェックボックス

JavaScriptやjQueryを使用すると
フォームで入力された文字や選択された項目を取得することができます。

基本こちらのHTMLを使用する前提で行っていきます。

```html:html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
    <script src="./script.js"></script>
    <title>jQuery Checkbox Practice</title>
  </head>
  <body>
    <form>
      <h1>果物を選んでね</h1>
      <div>
        <span>好きなだけ選べるよ</span>
      </div>
      <label>
        <input type="checkbox" value="シナノスイート">
        りんご
      </label>
      <label>
        <input type="checkbox" value="アボガド">
        バナナ
      </label>
      <label>
        <input type="checkbox" value="ホワイトグレープフルーツ">
        みかん
      </label>
      <div>
        <input type="submit" value="送信">
      </div>
    </form>
  </body>
</html>
```

# submit
submitイベントは、フォームが送信される時に呼び出されるイベントです。

```js:submit
$(function() {
  $("form").on("submit", function() {
    console.log("送信ボタンが押されました");
  });
});
```

コンソールに「送信ボタンが押されました」と表示されるはずですが、
すぐにコンソールから消えてしまいます。
理由は、formタグのsubmitボタンが押され、ページが新しく更新されるからです。
formタグのaction属性を指定していないので、同一ページにリダイレクトされています。

# preventDefault()
preventDefault() は、ブラウザが最初から持っているアクションをキャンセルするメソッドです。

```js:preventDefault()
$(function() {
  $('form').on('submit', function(e) {
    console.log('送信ボタンが押されました');
    e.preventDefault();
  });
});
```

2行目の関数の引数にeと追記しました。
イベント内の関数の第一引数に自動でイベントオブジェクトが渡されます。イベントオブジェクトには、イベントの発生元の要素や押されたキーの情報などが入っています。
ここで取得したイベントオブジェクトに対して、preventDefault()を使用することで要素のイベントをキャンセルすることができます。

# イベントオブジェクト
イベントオブジェクトとは、イベントに関する情報を持つオブジェクトです。
targetやkeyCodeなどのプロパティを使うことで
イベントの発生元の要素や押されたキーの種類などを知ることができます。

コンソールで確認することはできます。

# find("セレクタ")
Rubyで使用するfind()メソッドと用途は同じです。
しかし、1点大きな違いとして、Rubyのfind()メソッドは最初に該当する1つだけしか取得ができませんでしたが、
jQueryのfind()メソッドは、引数にセレクタを指定して、合致するセレクタを全て取得します。
セレクタの属性は、[]を使用します。

```js:find("セレクタ")
$(function() {
  $('form').on('submit', function(e) {
    let checkboxes = $(this).find('input[type="checkbox"]');

    e.preventDefault();
    alert('あなたが選んだ果物:\n' + checkboxes);
  });
});
```

# each()
引数に要素番号を指定することが可能です。

# prop()
prop() は、要素のプロパティを取得するメソッドです。

# attr()
prop()と似ていますが、attr()は要素の値（バリュー）を取得することができます。

# 総まとめ

```js:まとめ
$(function() {
  $('form').on('submit', function(e) {
    let output = ''; //出力する値を文字列の変数（output）で定義しています。
    let checkboxes = $(this).find('input[type="checkbox"]'); //チェックボックスを取得して変数checkboxesに代入しています。
    checkboxes.each(function(i, checkbox) { //each文を使い、checkboxesの中の要素を取り出します。
      checkbox = $(checkboxes[i]);
      if (checkbox.prop('checked')) {
        output += checkbox.attr('value') + '\n';
      }
    });
    e.preventDefault();
    alert('あなたが選んだ果物:\n' + output); //outputをalertで出力すれば、選択された果物を出すことができます。
  });
});
```

重要な箇所を重点的に解説します。
・7行目
　prop()を使って取り出した要素がチェックされているかどうかを確認します。7行目のif文において、要素がチェックされている場合はtrueが返り値になります。trueの場合、チェックされている要素の値だけを3行目で文字列の変数として定義しておいたoutputに+=を使って追加代入することができます。
コーディング後のセルフチェックや検品のための手順やツールについて

コーディング時のミスやセルフチェックについてまとめてみた

コーディング時にミスをしやすい人の特徴

> 1.自分のコーディングした箇所を覚えていない。
> 2.自分が作業した箇所は完璧だと思い込み、チェックが甘い。
> 3.時間に余裕がなく「えいやっ」とディレクターに完了報告をしてしまった。

# 検品項目（修正案件）
ブラウザ：Chrome

## １）閉じタグチェック
アドオン「HTMLエラーチェッカー」を使用して、閉じタグ忘れがないかチェックする
https://chrome.google.com/webstore/detail/html%E3%82%A8%E3%83%A9%E3%83%BC%E3%83%81%E3%82%A7%E3%83%83%E3%82%AB%E3%83%BC/ohdllebchmmponnofchalfkegpjojcaf?hl=ja
<img width="630" alt="スクリーンショット 2019-12-03 1.28.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254652/d56d58d0-4833-533e-81cb-cb1a7a573de3.png">

## ２）validatorチェック
W3CのサイトにてHTMLのvalidatorをチェックする
・HTMLチェック
https://validator.w3.org/#validate_by_input
・CSSチェック
https://jigsaw.w3.org/css-validator/#validate_by_input

## ３）タイトル・ディスクリプション・キーワード（TDK）・見出しをチェック
TDKが正しいか。見出しのアウトラインが正しいかチェック。
https://www.pixelimage.jp/blog/2015/08/bookmarklet_seo.html
h2のあとにh4がきたらダメよ。
<img width="618" alt="見出し.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254652/541e98bf-cbd5-8a99-6a1a-547590ed957a.png">

## ４）全角半角チェック
・アドオン「Super Highlight Search」
https://chrome.google.com/webstore/detail/super-highlight-search/mnnlboenenpikhopbchpjikllhibfocl
[Ａ-Ｚａ-ｚ０-９  ]
<img width="326" alt="スクリーンショット_2019-12-03_1_26_49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254652/4d19b4f0-a5bf-81ac-5b22-e607655d47ad.png">


・ブックマークレット
https://nacchi.org/2019/04/11/frontend-bookmark/#toc5

## ５）半角スペースチェッ ク
「Ctr+F」で半角スペースをページ内検索して、余計なスペースを削除する。（文言の途中にあったりする）
<img width="766" alt="半角space.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254652/971f4b88-7cb1-a517-7662-193dadceab88.png">

## ６）原稿とテストの文言を比較
difffを使用して、文言を比較する。
https://difff.jp/
「diff テキスト 比較」で探すと色々なツールが出てきますね・・・

## ７）本番とテストの文言を比較する。
本番のテキスト部分を原稿に置き換えて比較する。
差分が0になればOK
https://difff.jp/
※変更箇所以外を削除してしまったり、余計な文字を記述していないかチェックするために行う。

## ８）リンク
記述したリンクはクリックしてチェックする。target_blankの付与もチェック。

## ９）画像のALT
全て正確に入れること。
https://nacchi.org/2019/04/11/frontend-bookmark/#toc4

## １０）IEやiPhone、Androidで実機チェック
とくにページを作成したときの実機確認は必須項目です。
RubyでビッグデータのETL

# 概要
[分析基盤向けデータ統合サービスtrocco](https://trocco.io/lp/index.html)では、RubyでETLを行う機能があります。
この記事では、ETLの利用方法や、簡単なパフォーマンスを調査してみます。

# troccoとは
[https://trocco.io/lp/index.html](https://trocco.io/lp/index.html)

* 分析基盤向けのデータ統合サービス
* 分析に必要な様々なデータ(DB/ストレージ/SaaS/API)を、DWHに統合可能
* DWH自体の管理機能や、運用保守サポート機能も豊富

# Ruby ETLの仕組み
troccoは内部的に[Embulk](https://github.com/embulk/embulk)というOSSを利用しています。
Embulkには、Rubyで変換処理を記述できる[embulk-filter-ruby_proc](https://github.com/joker1007/embulk-filter-ruby_proc)というプラグインがあり、troccoはこのプラグインに対応しています。
そのため、行ごと・列ごと・複数行(page)ごとの変換処理を実現可能です。

# Ruby ETLの利用方法
ざっくり以下の流れでETLが実現できます
1. 変換処理(Rubyスクリプト)のコーディング
2. Rubyスクリプトのレビュー・転送設定に登録
3. 転送設定プレビュー
4. 転送ジョブ実行

## 1. 変換処理(Rubyスクリプト)のコーディング
以下のようなETL用テンプレートをダウンロードします

### 列ごとの処理用テンプレート
1列の値だけを変換する場合、こちらのテンプレートを利用します。
列内の値が`value`という変数に入ってくるので、その値を利用して処理を記述します。

```ruby
->(value) do
 # Implement ETL here
 # return value / 100.0
end
```

### 複数行(page)ごとの処理用テンプレート
複数の列を利用して処理を行う場合は、こちらのテンプレートを利用します。
`records`という引数に、複数レコードが`Hash`の配列が入って来ます。

```ruby
->(records) do
 records.each do |rec|
 # Implement ETL here
 # rec['cvr'] = rec['cv'] / rec['click'].to_f
 end
 records.dup
end
```

今回は複数行処理用のテンプレートを利用し、以下のような簡易ETL処理を記述しました。

```ruby
->(records) do
 records.each do |rec|
 rec['count'] = rec['count'].to_f / 2
 end
 records.dup
end
```

## 2. Rubyスクリプトのレビュー・転送設定に登録
変換処理を記述したRubyスクリプトを、troccoの転送設定に登録します。
残念ながら、現時点ではtroccoの画面から直接コーディング・スクリプトの登録を行うことは出来ず、Slackなどでサポートに依頼する必要があります。
（画面からの登録は現在開発中とのこと）

実際にSlack経由で依頼した所、数時間でレビュー・登録が完了しました。
登録されたスクリプトは画面から確認することが出来ます。
![転送詳細設定___trocco.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61228/7ea4e038-a96f-8036-b8be-c59f8b3db280.png)


## 3. 転送設定プレビュー
早速「設定を保存して再度プレビュー」をクリックし、ETLが正しく動いているか確認を行っていきます。
![ruby_proc_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61228/810e0f4f-d578-aa63-78fc-e5d86bbc9b12.png)

## 4. 転送ジョブ実行
画面からアドホックにジョブを実行する事が出来るので、試しに実行してみます。
troccoではCPUやメモリを好きな量割り当てることが出来ますが、今回はデフォルトのCPU2コア、メモリ2GBのコンテナで試しました。
<img width="1791" alt="trocco_etl_ruby.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61228/f9f879f7-1c50-e849-f771-50e9a647de6c.png">


# パフォーマンス
試しに70GB(約2.2億レコード)のS3上CSVファイルを入力とし、ETLにかかる時間を計測してみました。
念の為、ETLを行わない転送と、ETLを行った場合の2パターンで計測しております。

デフォルト環境なのですが、結構速いです。
ETLを行うと、その分処理オーバーヘッドが追加されるので時間はかかりますが、全然許容範囲内ではないでしょうか。

||Ruby ETL無し|Ruby ETLあり|
|:---:|:---:|:---:|
|1回目|74分|146分|
|2回目|72分|158分|
|3回目|79分|152分|
|平均値|75分|152分|

# まとめ
* RubyでETLが書けるため、簡単な変換処理だったら比較的楽に記述可能
* 転送速度は速いため、ある程度のデータ規模まで耐えられそう（CPUのコア数を上げるオプションを利用すればもっと速くなる）
* 分散処理フレームワーク(Sparkなど)を使わずとも、troccoで気軽に変換処理が書けるのは嬉しいですね！
[https://trocco.io/lp/index.html](https://trocco.io/lp/index.html)