OmniAICreator/Qiita-1.07M huggingface dataset:
Node.js でECHONET Liteを送受信

#はじめに
Node.jsを使ってECHONET Liteのデータを送受信するプログラムの作成

#UDP受信

UDPの受信のために、dgramのモジュールをインストールする

```
$ npm install dgram
```

以下サンプルコード

```javascript:receive.js
var dgram = require('dgram');

//ECHONET Liteのポート
port = 3610

//コールバック関数を設定する
sock = dgram.createSocket("udp4", function (msg, rinfo) {
    console.log(rinfo["address"]);　//ipアドレスのみを表示
    console.log(msg);
});

sock.bind(port, '0.0.0.0');
```

マルチキャストに対応したサンプルコード

```javascript:mult_receive.js
var dgram = require('dgram');

port = 3610

sock = dgram.createSocket("udp4", function (msg, rinfo) {
    console.log(rinfo["address"]);
    console.log(msg);
});

sock.bind(port, '0.0.0.0',function(){
    sock.setMulticastLoopback( true );
    sock.addMembership("224.0.23.0");
});
```

#UDP送信
ECHONET Liteの送信は [echonet-lite モジュール](https://www.npmjs.com/package/echonet-lite)が使いやすい


```
$ npm install echonet-lite
```

機器の探索のためのサンプルコード
受信しながら以下を実行すると、同一lan上のECHOENT Lite機器が返答する。

```javascript:search.js
// モジュールの機能をELとして使う
var EL = require('echonet-lite');

EL.search();
```

#参考
[echonet-lite モジュール](https://www.npmjs.com/package/echonet-lite)

[node.jsでUDPサーバーを作る1](http://nantekottai.com/2011/08/06/udp-server-with-node-js/)
初心者の1人エンジニアがチーム開発で学んだgithubの使い方！

#背景
私は趣味でプログラミングを始め、一人でrailsでwebアプリを開発している。
herokuなどを使用するためにgithubを利用しているが、使い方を理解せずなんとなくで使っていた。
githubの使い方を検索すると、gitのコマンド等を解説する記事はよくあるが、hub使い方や共同開発のフローについて説明する記事は意外と少ないと感じた。
最近チーム開発を体験する機会があり、その時にGitHubの使い方を少し学ぶことができたので備忘録と意味もこめ、まとめて記事にした。

最後に、実際にgitのブランチの整理をやった時の手順や注意点を書いた。

#今までの（おかしな）使い方
一人で開発していると共同作業というのがないので、いくらめちゃくちゃにリポジトリやブランチを作っても、だいたい自分は覚えているので困ることはない。
今までの私のgithubの使い方は、とりあえずプッシュしてherokuのサーバーにあげて、動かしたときにエラーが出たら、ひとつ前のコミットからブランチを作りそれをHEADにして開発を続け、またエラーが出たら、ひとつ前のコミットからブランチを・・・・という感じで使っていた。
その結果、ブランチは20個を超え、masterは遥か昔の状態で止まっているという状況である。
私のように一人で開発している初心者の中には、似たような状況の人がいるのではないかと思う。

#学んだ正しい使い方
正しい使い方というタイトルだが、実はこれが正しい！というのは存在せず、会社やチームによって使い方はそれぞれである。
今回記事にするのは、私が学んだ方法であり、たくさんの方法の中の1つである。

##ブランチの構成
基本的には本番環境のmasterと開発環境のdevelopmentの2つのブランチで構成される。そして、developmentは基本的にはmasterのコピーである。
そして、以下のようなフローで開発する。

**1. リモートdevelopmentをローカルにpull**
**2. ローカルdevelopmentから機能名や名前で作業ブランチを作る。**
**3．作業ブランチ内で機能が完成したら、ローカルdevelopmentにmarge。**
**4．ローカルdevelopmentをリモートdevelopmentにpush。**
**5. リモートdevelopmentでテストして、問題がなければ、本番環境（master）にmerge（pull request）。**

図にすると以下のような感じ。（うまく表現するのがむずかしいが...）

![github_figure.png](https://qiita-image-store.s3.amazonaws.com/0/216422/355508c8-17c6-9a0a-6224-01a60abb76ec.png)

今回の方法はmasterとdevelopmentの2階層であるが、チームによってはこれがさらに増えたり、または全く異なる方法を採用している。


#感想（実際にやる前）
今回記事にした方法は理解しやすく、単純であるため、一人で開発を行う際にも取り入れられると感じた。
これから、自分の開発環境を整えていこうと思う。

#実際にやってみて（注意点）
この記事に書いたようなgitの構成に自分の環境を整理するためにマージ作業をしたときの手順と苦戦した点についてまとめた。（想像していたよりも相当苦労した）
source treeを使い作業したため何とかなった。
もし、黒い画面（コマンドライン？）で作業していたらかなり厳しかったと思う。
以下が、実際に行った手順。

##1．過去にほったらかしにしていたmasterを現在の最新ブランチであるtestにマージする。
この作業をしたときにまず、私はマージ（merge）の概念を誤解していた。
**誤解：masterをtestにマージするとtestの内容がmasterにコピーされる**
これは、完全に誤解である。
私の場合、エラーが発生したら、そのひとつ前のコミットから新しいブランチを作り、それをメインブランチ（HEAD）にするということをしていたため、このような考えのもとマージしたら、**testの内容だけでなく、大量の必要のないコード（testブランチを切った後にmasterで追加してエラーの原因となったコード）が大量にmasterに追加されてしまった。**

そう！
マージとは**複数の編集内容を合体させるという意味なのだ**
マージでは同じ場所を編集して、コンフリクトが起きない限りは変更がどんどん追記される。
チーム開発をやったときの開発の方法を思い出せば当然である。
というか、そもそもgitは差分を管理するツールであった...。

なので、マージしたコミットを取り消して、testブランチを無理やりmasterブランチに（名前変更）する作戦に変更した。


##2．test以外のブランチを全部削除（かなり危険なやり方）
かなり強引なやり方であるが、ほかに方法が思いつかなかったので、現在のHEADであるtest以外のブランチをローカルでもリモートでも削除した。


##3．testのブランチ名をmasterに変更
前の手順でmasterブランチを削除したので、その代わりにtestブランチをmasterにリネームする。

```
git branch -m test master
```
これで無事にメインのブランチがmasterブランチに！

##4. masterブランチからdevelopmentブランチを作成
masterブランチからdevelopmentブランチを作成。
そして、両ブランチをリモートにプッシュ。

##5．developmentブランチから作業用のブランチを作成
ローカルのdevelopmentブランチから作業用ブランチ（work-stationブランチなど）を作成。


##開発方法
これで、ブランチ整理は無事終了！！！

今後の開発方法としては、
1. 作業ブランチで編集
2. ローカルのdevelopmentにマージ
3. developmentをリモートにプッシュ
4. githubでdevelopmentブランチからmasterへのプルリクエスト（pull request）を作成し、マージ。

という感じになる。

注意点としては、ローカルでmasterにマージしないこと。
ローカルでは作業ブランチとdevelopmentブランチのみを使用して、masterへのマージはgithub上で行う。
これで、開発者が複数になっても対応できる。

#感想（実際にやった後）
下調べしていた時は簡単にできると思っていたが実際にやってみると、難しく非常に難しく、ハラハラドキドキした。笑（心臓に悪い）

かなり苦戦したが、gitは共同開発を行う上で必須な技術の一つであるので、これを機会に理解を深めることができて良かった。

今回はかなり無理やりの手順で行ってしまったので、もし、より良いgit整理の方法があれば、コメントにてお教えいただければ助かります。

そして、この記事が私のようなgit初心者の参考になれば幸いです。
【初心者歓迎！】Ruby on Railsもくもく会@新橋　に参加させていただきました

#今日やることと動機

最近、Reactを始めた(とりあえずReactTutorialを終えたくらい)ので、TodoListのアプリケーションのフロント側を作ってる。
APIでDBからデータを引いてきて画面に表示したい。
せっかくなのでRailsで実装してみることにした。

#成果
##APIモード

Rails5には"APIモード"なるものがあるらしいので使ってみる。

```rails new application --api```

ディレクトリ構成も従来よりシンプル。
標準でjsonResponseを返却するようになっている。

とりあえず試してみた。

```
bundle exec rails scaffold task
rails s
```

http://localhost:3000/tasks
にアクセス。

jsonのレスポンスが帰って来てるのが確認できた。
(やりたいことがほぼ一瞬で終わった。)

##DBをMysqlに変更する

Gemを入れる。

```ruby:/Gemfile
gem 'mysql2'
```

config/database.ymlに以下を記述

```yaml:/config/database,yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password: rd384ktk
  host: localhost
  timeout: 500
```

##React側からリクエストを送信する

http://localhost:3000/tasks
にPOSTする。

データをインサートできる。


##seedデータを分割する

```bundle exec rails db:seed```で実行されるseedingをModel毎に分けたいので仕組みを追加。

db/seeds.rbに以下を記述

```db/seeds.rb
development_seed_files = Dir[ Rails.root.join('db', 'seeds', '*.rb') ].sort

ActiveRecord::Base.transaction do
  development_seed_files.each do |file|
    load(file)
  end
end
```

これでdb/seeds配下のファイルを一括で投入できる。
MacOSXでスペース入りの複数ogg/wavをコンバートする方法

MacOSXでスペース入りの複数ogg/wavをコンバートする方法

## 準備
まず[vorbis-tools](  )をいれます

```
$ brew install vorbis-tools
```

## Wav->Ogg(Encode)
```
$oggenc hoge.wav
```

ビットレートを64kbpsに指定する場合は-bオプション

```
$oggenc -b 64 -o hoge.ogg hoge.wav
```

## Ogg->Wav(Decode)

```
$ oggdec fuga.ogg
```

## MacOSXでスペース入りの複数ogg/wavをコンバートする

サンドデザイナーさんがよくやる「スペースいれちゃった☆」場合は心をおちつけて

エンコード

```
$find . -type f -name "*.wav" -print0|xargs -0 oggenc
```

デコード

```
$find . -type f -name "*.ogg" -print0|xargs -0 oggdec
```
ビットレート48kって約束したのにうっかり混ざってそうな時には

```
$find . -type f -name "*.ogg" -print0|xargs -0 ogginfo|grep -E '^Rate: 44100'
```



##参考
- [xargsマニュアル]( https://linuxjm.osdn.jp/html/GNU_findutils/man1/xargs.1.html )
- [ Vorbis tools(コマンドライン OGG形式関連 ツール) ]( http://www.xucker.jpn.org/pc/vorbis_tools.html )
アカウント毎にAWSコンソールのヘッダ/フッタ色を変えるChrome拡張を作った

## 2018/7/26 追記
[Chrome Web Storeにて公開](https://chrome.google.com/webstore/detail/colorawsframe/egnkjmppojiamgimoclpjopdohjejbjf)しました:wave:


## なにこれ

https://github.com/miztch/color-aws-frame
AWSマネジメントコンソールのヘッダ/フッタの色を変えるChrome拡張です。

<img src="https://qiita-image-store.s3.amazonaws.com/0/55305/012d2f59-d94e-bbfb-5c75-d81a7c93012d.png" width = 80%>

<img src="https://qiita-image-store.s3.amazonaws.com/0/55305/53c38fa5-ccec-30a9-b33a-acecd7197ceb.png" width = 80%>



こんな感じで、ログインしているIAMユーザまたはアカウントごとに色が変わります(フッタも変わります！)
御存知の通りAWSマネジメントコンソールのUIはどのアカウントでも変わらないし変更できないので、複数のアカウントにログインして作業する機会があると、「今どのアカウントの操作してるんだっけ？」と不安になったり~~<font color="lightgray">、実際操作を間違えたり</font>~~したので予防のために作ってみました。

## どうやったの

色を指定する部分は、@uiureo さんの chame を利用させていただきました。
https://github.com/uiureo/chame

上記を利用して、ユーザ/アカウント名→カラーコードの変換を行い、その色をヘッダ/フッタ部分に適用します。

```javascript
(function() {
  'use strict';

  // ユーザ / アカウント名の取得
  var account = document.querySelector("#nav-usernameMenu .nav-elt-label").textContent;

  // ユーザ / アカウント名 → カラーコードに変換
  var background = account.toRGBCode();
  
  // ヘッダ・フッタ部分の色を変える
  var selectors = ["#nav-menubar", "#nav-menu-right", "#console-nav-footer"];
  selectors.forEach(function(s) {
    document.querySelector(s).style.background = background;
  })

  var e = document.querySelectorAll(".nav-menu");
  for (var i = 0; i < e.length; i++) {
    e[i].style.background = background;
  }
})();
```

## 使い方

- git clone

```
$ git clone https://github.com/miztch/color-aws-frame.git
```

- Chromeで`chrome://extensions`を開く
- 「デベロッパーモード」にチェック
- 「パッケージ化されていない拡張機能を読み込む」
- cloneしたディレクトリを選択

## 今後の課題

- 一般的にはIAMユーザでログインするので、`(自分の名前) @ (AWSアカウント名)`がカラーコードに変換されてヘッダ/フッタ色が決まるが、`(自分の名前)`部分は環境問わず同じなので、結果として出てくる色が似通ってしまうことがあるのをなんとかしたい
    - 実用的には設定画面を付けて、色を自由に変えられるようにする……？
    - 文字列からランダムに色が出てくるところも楽しいので、別物として作るかも......
Linux：ランキング取得コマンド


```linux-config
grep ' connect from ' <ファイルパス> | awk '{print $8}'|sort |uniq | wc -l
```

`awk`：
`sort | uniq`：
`wc -l`：

参考URL：https://qiita.com/qiitamatumoto/items/8c4341d0af9958543db4


```linux-config
cat res_1 |sort -n -r |head
```
hashlimitについて

・iptablesを使用して一定時間あたりの接続数を制限する。

・特定のIPに対して、接続可能時間を制限。
`--hashlimit-mode srcip,dstport`
対象グループをまとめる
`--hashlimit-burst 30`
グループごとにアクセスできる秒数を指定。初期値は30回
`--hashlimit 1/m`
アクセスできる回数の追加のペースを決める。

上記の設定であれば、30秒連続でアクセスしたら、次の30秒待たなければアクセスできない。
※30秒連続アクセス→30秒待つ→1回アクセス可能

クライアントごとに制御可能なので、特定のIPからの連続した攻撃に対応可能

参考URL：https://qiita.com/homihomu/items/778770a1c1b3b2371fe0
Vue.jsでclassを動的につける

`v-bind:class="{ mymessage:isMyMessage }"`この部分でisMyMessageがtrueかfalseかでmymessageクラスをつけたり外したり出来る。

```html
<p v-bind:class="{ mymessage:isMyMessage }">{{ message.messageText }}</p>

<script>
export default {
　data(){
 　  return {
   　   isMyMessage:true
   　}
　}
}
</script>

<style>
   .mymessage {
        text-align:right;
    }
<style>
```

(参考)
[vueとfirebaseでブラウザチャット](https://qiita.com/nakadoribooks/items/a5c73580039c99a117dc)
[Vue.js - HTML クラスのバインディング](https://jp.vuejs.org/v2/guide/class-and-style.html)
Linux：ログの中の検索ワードのカウント

```linux-config
cat <ファイルパス> | grep <"検索したい単語"> | wc -l
```

`cat`：ファイルの中身を表示
`|`：コマンドを連結させる記号
`grep`：指定した単語の取得

```linux-config
grep <"単語"> <ファイル名>
```

grepオプション
>`.`	改行文字以外の任意の1文字
`*`	直前の1文字の0回以上の繰り返しに一致。直前の文字は正規表現でも構わない
`^`	行の先頭を表す
`$`	行の末尾を表す
`[ ]`	かっこ内の任意の文字に一致。ハイフン（-）で範囲指定もできる。かっこ内の最初の文字に^を使用すると，意味が逆転する
`+`	直前の文字の1個以上の連続
`?`	直前の文字の0または1文字にマッチ
`pattern1|pattern2`	pattern1またはpattern2のいずれかにマッチ
`(pattern)`	patternをグループ化する
`\`	正規表現に使われる記号を普通の文字として扱う
引用URL：http://itpro.nikkeibp.co.jp/article/COLUMN/20060227/230786/

`wc -l`：指定したファイルの行数・単語数・バイト数を表示するコマンド
オプションは下記参照
`-c`バイト数のみを表示
`-L`	もっとも長い行の長さを表示
`-l`	行数のみを表示
`-m`	文字数のみを表示
`-w`	単語数のみを表示
Xdomein(の無料サーバ)からXserverへ サーバー移転方法

<h2>どうでもいい前書き</h2>

業界６年目にして初めて自分用にサーバーを契約した２６歳
WEBディレクションのようなことやデザインのようなことはしてきたけど
コーディングに関してはできなくもないけどほぼノータッチ
サーバー周りも他部署がやってしまうのでノータッチ

今更感しかないけどこれ以上遅くなってもあれなので初心者の備忘録です

<h1>Xdomein(の無料サーバ)からXserverへ サーバー移転方法</h1>
Xdomeinで独自ドメインだけ取得して無料サーバを使ってました
無料サーバはスマホ版のみ広告が出ます
それがどうも気になるようになりいい機会なのでXserverで契約

ファイル移動するも、いつまでも前のサーバーデータが出る！
あたりまえ体操ですね
サーバーの移転作業が必要でした

https://www.xserver.ne.jp/order/order_transfer_server.php

現時点でこのページの
１）申し込み
２）ドメイン設定
３−１）データの移行（今回wpの移行は無し）
ここまで終わってる状態

さて、<b>３−２）ホームページの表示・動作確認</b>をします

<img width="776" alt="スクリーンショット 2018-01-20 15.53.10のコピー.png" src="https://qiita-image-store.s3.amazonaws.com/0/203182/5502983c-a833-b2f5-2f94-22fcbfc6412a.png">
<b>うわあああああ</b>

はい　サーバーの設定が反映されてません
なぜならこのドメインには前のサーバーが紐付いたままだから
（一つのドメインに複数サーバー紐付けする方法もなくはないですが、今回は割愛します）

ここで必要なのが<b>３−４）ネームサーバーの変更</b>です
<b>ネームサーバーの変更</b>は移転先がXserverである場合に限らず
どのサーバーでもこの作業は必要なようです

現在の私のドメイン管理者がXdomeinなので、Xdomeinの管理パネルから
ホーム > 契約ドメイン一覧 > 対象ドメインのドメイン管理 > ネームサーバーの確認・変更

<img width="782" alt="スクリーンショット 2018-01-20 16.16.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/203182/6f9f5369-a20f-c243-5c47-65161e074eba.png">
<img width="784" alt="スクリーンショット 2018-01-20 16.22.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/203182/9f0816bd-d203-053c-d622-9693c8ef4caf.png">

<b>ネームサーバーを変更する</b>から新しいサーバーのネームサーバーを入力し、確定

あとは数時間待てばサーバーの移転が完了します
<img width="761" alt="スクリーンショット 2018-01-20 17.01.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/203182/6c71006a-c1e6-e2b5-a86b-781bb0fb5a2c.png">

それではまた
fsutilでファイル作成

`fsutil`：ファイル作成コマンド

```winbatch
fsutil file createnew <ファイル名> <サイズ>
```

10GBちょうどのファイルを作成したい場合は下記

```winbatch
fsutil file createnew test 10737418240
```

参考URL：http://www.atmarkit.co.jp/ait/articles/0209/28/news002.html
Linux：ファイル取得（wget）→解凍（tar）

```linux-config
yum install wget
wget `ファイルのURL（https://）`
tar xf `ファイル名`
```

`yum install コマンド`：すでにインストール済みだったらいらない
`wget URL`：インストールしたいファイルのURL
`tar xf`：ファイルの解凍コマンド

>
x : eXtract(展開)
v : verbose(言葉の多い、展開内容を表示)
z : gzip対応
f : file から展開(デフォルトはテープデバイスになってるので。)
引用元：https://qiita.com/ironsand/items/b53301eb07e0f6199126
テスト（単体）を走らせるときにデータベースをどこで削除(DELETE FROM)するか

# 序
リハビリを兼ねて記事を書いてみます。現在大学生活をながらベンチャー企業でプログラミングのインターンをしています。個人でプログラミングをするのとグループでするのでは驚くほど違いがたくさんありますが、テストもその一つだと思います。（まあ今まで一人で書いてきたコードなんて、なんか欲しい、でちょちょっと作ってほぼほぼメンテナンスなんかしないで放置みたいなものばっかでしたし単体テストなんか書いたことなかったんですけど）

いま会社で作っているサービスでも単体テストを走らせていて１から勉強して自分でも書いていたのですが、そこで仕組みというか順序なんかで気になることがあったので記事を書くことにしました。

# ユニットテスト中のデータベースの扱い
記事を書きながら軽く、他ではどうなのか調べていますが、僕が現在いじっているサービスでは文字通り単体でテストが動くように、テストごとに実際にデータベースに必要な値を挿入して（テスト用のデータベース）、テストが終わったら削除するという実装になっています。

```
てすと１ {
     (データベースから挿入 ユーザー情報とか)

          テスト実行

     (データベースから削除)
}
```

毎回同じ状態にする（復元）ためにデータベースから削除する、ところではすべてのデータをバッサリ全部DELETEしています。それぞれのテストで必要なデータが同じだったらある一定の状態に保つってのもありなのかもですが、、

# 失敗した時の対応
上の形式でテストを組んでいるのできっちり、他のテストに左右されずに、単体でも問題なく（ログイン処理の途中とか複雑な設定でも）動いてくれます。

が、

問題は失敗したときにあります。

```
てすと１ {
     (データベースから挿入 ユーザー情報とか)

          テスト実行 ==> "えらー！"

     (データベースから削除)
}
```

まあ見ればわかりますがテストの最中のアサート文でエラーが発生するので削除までたどり着かず次のテストが走り出してしまうわけですね。

これだとまっさらな状態（一番最初に走らせているときなど）と同じ状態ではなくなってしまうので、十中八九テスト落ちるし、仮に通ったとしても単体テストの趣旨からはずれてしまいます。

# リセット処理（削除処理）はテストの頭に書く
```
てすと2 {
     (データベースから削除)
     (データベースから挿入 ユーザー情報とか)

          テスト実行

}

```
単体のテストを書くときは頭に削除処理を書いておくと(もしくは挿入の前にリセットを強制するとか)失敗が連鎖したり予期せぬ動作が減って幸せになれるんじゃないかなと思いました。幸あれ


# 結び
この話を書いた背景ですが、テストの仕組みをよくわかっていないまま、ガリガリテスト動かして、周りのコード真似して、エラーが出たらそこを直すっていう風にカバレッジを上げるのを最優先で進めてたんですが、テストを全部実行すると何のエラーもなく全部通るか、バタバタといくつもテストが落ちて問題箇所が探しにくい、、ということがよくあって、いままでは-vvxみたいにテストが落ちたら強制終了、みたいにして問題箇所を修正していたのですが、本質的じゃないなあと思って、残しておこうと思いました。




適当なこと書いてる部分があったらごめんなさいすぐなおします
LinuxのプロセスのCPU使用率をcpulimitで制御する

Linuxではcpulimitを用いて、プロセスのCPU使用率を指定した値に制限することが可能。

プロセスID、プログラム名、絶対パスによる指定が可能

>参考URL：https://www.xmisao.com/2014/07/10/cpulimit.html

Elasticesearchではパフォーマンスを変更することが可能

>参考URL：https://www.elastic.co/jp/blog/performance-considerations-elasticsearch-indexing
Ansibleについてまとめ

#Ansibleとは
ネットワーク自動運用プロセス

>「Playbook」と呼ばれるファイルに記述した設定内容をサーバーやネットワーク機器に正確に反映させる機能を持っており、運用管理者が多数のサーバーやネットワーク機器に対して、同一の設定を繰り返し行う必要がなくなる。
引用元：http://businessnetwork.jp/tabid/65/artid/5847/page/1/Default.aspx
WinlogbeatとElasticsearchでWindowsイベントログ収集可視化

#Winlogbeat
elastc社のソフト
Windowsのイベントログを収集し、Elasticsearch（elastic社のソフト）へ投げるもの

・ログ収集ソフトメモ
`syslogd`
`fluentd`：Treasure Dataが開発。ログの収集方法やログの記録先などを柔軟にカスタマイズ可能
`Logstash`
`Apache Flume`

#Elasticsearch
Elastic 社が開発しているオープンソースの全文検索エンジン
`全文検索エンジン`
大量にある文書の中から目的のワードを持つ文書を検索するための検索エンジン

#Kibana
elastic社が開発
検索エンジン「Elasticsearch」と連携して使用するデータ解析/可視化ツール

#Shield
elastic社が開発
ロールに基づいたアクセス制御
AD連携可能

>参考URL：https://qiita.com/datake914/items/a89f01ffa3429f9be4c4
UITextFieldに入力する際，キーボードの上にprev, next, doneボタンを設置する方法

一応まんまコピペで追加すれば動くはずです。

はじめに結論から

# 準備
ViewControllerに`UITextFieldDelegate`を追加しておく

# コード

```swift
var currentTextField : UITextField?

func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
    
    let toolBar = UIToolbar()
    toolBar.barStyle = UIBarStyle.default
    
    toolBar.sizeToFit()
    
    currentTextField = textField
    
    let left = UIBarButtonItem(title: "<", style: .plain, target: self, action: #selector(tapLeft))
    let right = UIBarButtonItem(title: ">", style: .plain, target: self, action: #selector(tapRight))
    let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
    let done = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(tapDone))
    
    toolBar.items = [left, right, space, done]
    
    textField.inputAccessoryView = toolBar
    
    return true
}

@objc func tapDone(){
    self.view.endEditing(true)
}

@objc func tapLeft(){
    guard let tag = currentTextField?.tag, let nextTextField = self.view.viewWithTag(tag - 1) else{
        return
    }
    nextTextField.becomeFirstResponder()
}

@objc func tapRight(){
    guard let tag = currentTextField?.tag, let nextTextField = self.view.viewWithTag(tag + 1) else {
        return
    }
    nextTextField.becomeFirstResponder()
}
```

# コードについて
`#selector()`に引数が渡せないみたいなので苦肉の策で外の変数`currentTextField`に一度現在アクティブなtextFieldを預けています。
これ気持ち悪いのでなんか他にいい方法あったら変えたいです。
nabehideさんの<<python実行ファイル変換モジュール比較>>について

cx_freezeが自動的にPythonの実行環境をインストールしてくれるのは嬉しいですが、
その生成したexeファイルを起動する度に
毎回毎回解凍とPython実行環境のインストールを行うのでしょうか。

 nabehideさんの"python実行ファイル変換モジュール比較" 
https://qiita.com/nabehide/items/3b1b2b22674e9b76b20c
に紹介されている実験からすれば、
*.pyを実行するまでの前処理が数秒(second)から十数秒(second)もかかります!
(*.py自体の実行時間より遥かに大きい!!)

もしかして、最初の一回のみ環境インストールや解凍処理が行われるのでしょうか。
ヒントでも是非ご教授お願い致します。
docker & docker-compose使うやつまとめ（随時追加）

## 動いてるコンテナ一覧
```docker ps -a```

上記の[NAMES]がコンテナ名

## コンテナに入る時
```docker exec -it コンテナ名 bash```

## [Dockerコンテナ等一括削除するとき](https://qiita.com/mom0tomo/items/911b92cc18871f52a2a0)
```docker system prune```

## Dockerコンテナ立てるとき

```docker run -t <name> .

## コンテナ一括削除

docker rm $(docker ps -aq)

## docker-composeでデバッグ

- docker-composeに以下を設定 [参考](https://knowledge.sakura.ad.jp/5736/)

```
    stdin_open: true
    tty: true
```
The Japanese translation of "A Whirlwind Tour of Python" - Appendix: Figure Code

http://nbviewer.jupyter.org/github/jakevdp/WhirlwindTourOfPython/blob/master/17-Figures.ipynb

# 付録：図のコード
この節は、このレポートで現れる図を生成するために使われるコードを含みます。

```
In [1]:
%matplotlib inline
import matplotlib.pyplot as plt
In [2]:
import os
if not os.path.exists('fig'):
    os.makedirs('fig')
```

## Section 6: List Indexing
図はPythonのインデックスがいかに作用するかを可視化するのを支えます。

```
In [3]:
L = [2, 3, 5, 7, 11]

fig = plt.figure(figsize=(10, 4))
ax = fig.add_axes([0, 0, 1, 1], xticks=[], yticks=[], frameon=False,
                  aspect='equal')

for i in range(5):
    ax.add_patch(plt.Rectangle([i - 0.5, -0.5], 1, 1, fc='none', ec='black'))
    ax.text(i, -0.05, L[i], size=100,
            ha='center', va='center', family='monospace')
    
for i in range(6):
    ax.text(i - 0.5, 0.55, str(i), size=20,
            ha='center', va='bottom', family='monospace')
    
for i in range(5):
    ax.text(i - 0.5, -0.58, str(-5 + i), size=20,
            ha='center', va='top', family='monospace')
    
ax.axis([-0.7, 4.7, -0.7, 0.7]);

fig.savefig('fig/list-indexing.png');
```

![ダウンロード (2).png](https://qiita-image-store.s3.amazonaws.com/0/147303/b695d85d-f77a-3a48-c28d-d5fb508a472e.png)
The Japanese translation of "A Whirlwind Tour of Python" - Resources for Further Learning

http://nbviewer.jupyter.org/github/jakevdp/WhirlwindTourOfPython/blob/master/16-Further-Resources.ipynb

# さらなる学習のための資料
これは私たちのPython言語の駆け足の旅を結論づけます。もしあなたがここまでとおく読んでいるなら、あなたがさらに探求できるコードの構造とツールの範囲のアイデアと同時に、Python言語によって提供される本質的なシンタックス、セマンティクス、演算と機能のアイデアをもつことが私ののぞみです。

Pythonを使ったデータサイエンティストにとってより使いやすくなるPython言語のパターンや部分を網羅してきてますが、これは完全な紹介にはけっしてなっていないです。もしPython言語自体をより深く理解し、効果的に使う方法を理解したいなら、私がお勧めする役立つ資料がここにあります：

* Fluent Python by Luciano Ramalho. これはすばらしいオライリー本で、Pythonの最高の実践とイディオムを探求します、標準ライブラリもたいていの方法を得られることを含んでいます。
* Dive Into Python by Mark Pilgrim. これはPython言語の最初からの紹介を提供するフリーのオンライン本です。
* Learn Python the Hard Way by Zed Shaw. この本は、"やってみて学ぶ"アプローチを踏襲しています、そして、依然としてプログラマが学習できるもっとも役立つスキルになるかもしれないことを開発することをわざと強調します：つまり、グーグルで検索しても理解できないことです
* Python Essential Reference by David Beazley. これは700ページのよくかかれたモンスターです、実質的にPythonと組み込みライブラリについて知ることがすべて網羅しています。よりアプリケーションにフォーカスしたPythonの一通りは、"Beazley's Python Cookbook"を見てください。

よりデータサイエンスや科学的計算のためにPythonツールを掘るために、次の本をお勧めします：

* The Python Data Science Handbook by yours truly. この本は、このレポートが置き忘れているところを精密に開始します、そして、Pythonのデータサイエンスの積み重ねにおける本質的なツールの包括的なガイドを提供します、データマンジングと操作から機械学習まで。
* Effective Computation in Physics by Katie Huff and Anthony Scopatz, は、物理研究の世界をはるかにこえた人々に受け入れられます。科学的計算に対する順番に最初からの紹介で、このレポートで言及した多くのツールのすばらしい紹介を含みます。
* Python for Data Analysis by Wes McKinney, creator of the Pandas package. この本は、詳細においてPandasライブラリをカバーしています、同時にそれを可能とする他のいくつかのツールについて役立つ情報を与えます。

最後に、より広い人にさえ、そこでだされるものをみてみましょう、私は次をすすめます：

* OReilly Python Resources オライリーはPythonの世界の特別な題材とPythonそれ自体のたくさんのすばらしい本をとりあげてます。
* PyCon, SciPy, and PyData. PyCon, SciPyとPyDataカンファレンスは毎年何千という参加者を誘致していて、フリーのオンラインビデオによって毎年それらのプログラムの塊をアーカイブしています。これらはPythonそれ自体、Pythonパッケージ、そして関連する題材について学習するための信じられないほどの集合になっています。トークとチュートリアルの両方のビデオをオンラインで検索できます：前者はより短い傾向があり、新しいパッケージや古いものに対する新しい見方を網羅します。チュートリアルは、何時間かの傾向で、他と同じようにツールの使用に言及したものをカバーします。
正規表現を使ってSQL文の特定の関数を取り除く

# はじめに
SQL文を書いていたときに、既に書かれている関数をすべて取り除きたいことがありました。
そんなときに、手作業で消していくのではなく、サクラエディタの正規表現を使って一発で置換できる方法です。

# 正規表現を使って特定の関数を取り除く
例えば以下のようなSQL文からNz関数[^Nz関数とは]を取り除きたい場合。

```SQL
SELECT
	 Nz(aaa)
	,Nz(bbb)
	,Nz(ccc)
	,Nz(ddd)
	,Nz(eee)
	,Nz(fff)
	,Nz(ggg)
FROM
	table
```
サクラエディタの置換機能で、

```
置換前：Nz\((.*)\)
置換後：$1
```

と入力すればNz()だけが取り除かれます。
![0001.JPG](https://qiita-image-store.s3.amazonaws.com/0/230850/45f25630-0229-2889-864b-655b3e6a60ed.jpeg)

# 手順
詳しく手順を説明します。

SQLが記載されているサクラエディタの、**「検索」**タブから**「置換」**を選択します。（**Ctrl + R**でも可）
![0002.jpg](https://qiita-image-store.s3.amazonaws.com/0/230850/40b140ac-7585-f0f1-4265-4add3c92f5fa.jpeg)

置換前に「**Nz\\((.*)\\)**」、置換後に「**$1**」と入力し、
**正規表現**にチェックが入っていることを確認して、**すべて置換**を選択します。
![0003.jpg](https://qiita-image-store.s3.amazonaws.com/0/230850/6a4f9c75-ef75-1b78-9464-c29998b28ed0.jpeg)

Nz()が取り除かれます。

![0004.jpg](https://qiita-image-store.s3.amazonaws.com/0/230850/65432197-8cf6-feff-a8df-d39c7ada39ad.jpeg)

# なにがおこっているのか
それぞれの正規表現の特殊文字にどんな役目があるのかというと、
.は任意の1文字。
\*は直前のパターンの0回以上の繰り返し。
\（￥）はエスケープ文字で、\の次にある正規表現記号を普通の文字として扱います。
そして()でくくった中の文字列.*が$1という変数に代入されます。

```
Nz\((.*)\)
$1
```

なので、Nz(aaa)のaaaが$1という変数に格納され、マッチした文字列Nz(aaa)をaaaに置換することができるのです。

# １行に関数が複数ある場合
では、次のようなSQL文だと、どうなるか

```SQL
SELECT
	 Nz(aaa) + Nz(AAA)
	,Nz(bbb) + Nz(BBB)
	,Nz(ccc) + Nz(CCC)
	,Nz(ddd) + Nz(DDD)
	,Nz(eee) + Nz(EEE)
	,Nz(fff) + Nz(FFF)
	,Nz(ggg) + Nz(GGG)
FROM
	table
```

同じように置換すると、

```SQL
SELECT
	 aaa) + Nz(AAA
	,bbb) + Nz(BBB
	,ccc) + Nz(CCC
	,ddd) + Nz(DDD
	,eee) + Nz(EEE
	,fff) + Nz(FFF
	,ggg) + Nz(GGG
FROM
	table
```

こうなってしまう。
)が行の最後の)までマッチしてしまうのです。

なので置換の条件を

```
置換前：Nz\((.*?)\)
置換後：$1
```

とすれば、

```SQL
SELECT
	 aaa + AAA
	,bbb + BBB
	,ccc + CCC
	,ddd + DDD
	,eee + EEE
	,fff + FFF
	,ggg + GGG
FROM
	table
```

求めている結果になります。
?をつけると、直前のパターンのマッチの範囲を最小にすることができます。


# 【おまけ】関数が入れ子になっている場合
こんなかんじで関数が入れ子になっている場合はどうだろう。

```SQL
SELECT
	 CStr(Replace(Nz(aaa)," ",""))
	,CStr(Replace(Nz(bbb)," ",""))
	,CStr(Replace(Nz(ccc)," ",""))
	,CStr(Replace(Nz(ddd)," ",""))
	,CStr(Replace(Nz(eee)," ",""))
	,CStr(Replace(Nz(fff)," ",""))
	,CStr(Replace(Nz(ggg)," ",""))
FROM
	table
```

CStr関数を取り除きたいなら

```
置換前：CStr\((.*)\)
置換後：$1
```

Nz関数を取り除きたいなら

```
置換前：Nz\((.*?)\)
置換後：$1
```

と、すればそれぞれの関数だけ取り除かれるはずです。

ただ、Replace関数を取り除く場合は難しそうです。

```
置換前：Replace\(([^\(\)]*\([^\(\)]*\))[^\(\)]*\)
置換後：$1
```

とすればできますが、これだとReplace関数の中に()が2つずつある場合にしか対応できないし、

```SQL
SELECT
	 CStr(Replace(Nz(ggg),")",""))
FROM
	table
```

のように、カッコが混じればもう無理です。


# おわりに
正規表現たのしー！！



[^Nz関数とは]: Accessのクエリで使える関数。第1引数がNullの場合に長さ0の文字列を返す。
oFで3次元のattribute変数へのデータを送る際につまずいたこと

#始めに
こんにちは、最近は開発しながらyoutubeでPerfumeのライブ動画とか見てるんですけど、演出がマジでかっこいいですね。。さすがライゾマ。。見ているだけでモチベが上がります.はいでは本題に入りましょう。
この記事では、shader（VertexShader）内の三次元のattribute変数にC++コードからデータを転送する際につまずいたことを書いていこうと思います.この記事を読んでご意見などありましたらお願いします.

#そもそもattribute変数とは？？
この記事について検索をかけている方ならわかっていると思いますが、一応説明させていただきます.attribute変数とは頂点ごとに違うデータを格納できる変数になります.代表的な例とするならば、頂点情報、法線情報、色情報とか.わざわざ送らなくてもglsl内の組み込み変数で取得できる物もあるんですね.

#今回すること
vboにデータを格納した後、その法線情報をVertexShaderへ渡して、数値を確認するためにvarying変数を用いてFragmentShaderへ渡して色を確認していこうと思います.今回は簡略化のため面ではなく点の法線？ちょっと変な感じがしなくもないですが、それを使っていこうとおもいます.
以下のコードは　attribute変数周りの処理を書いているコードで全体の一部となります.

```shader.vert
#version120
attribute vec3 normal;
varying   vec4 vColor;
void main()
{
    vColor = vec4(normal, 1.0);
    vec4 eyeCoord  = gl_ModelViewMatrix * gl_Vertex;
    gl_Position    = gl_ProjectionMatrix * eyeCoord;  
}

```

```shader.frag
#version120
varying vec4 vColor;
void main()
{
    gl_FragColor = vec4(vColor);
}


```


```ruby:ofApp.h
float attNormal[Num*3];//法線情報を格納した転送用配列

```

```ruby:ofApp.cpp

for(int i = 0; i < WIDTH; i++){
        for(int j = 0; j < HEIGHT; j++){
            
            attNormal[(j * WIDTH + i)*3] = 0.0f;
            attNormal[(j * WIDTH + i)*3 + 1] = 0.0f;
            attNormal[(j * WIDTH + i)*3 + 2] = 1.0f;     
        }
    }



shader.begin();
   
attLocation[1]  = shader.getAttributeLocation("normal");
glEnableVertexAttribArray(attLocation[1]);
glVertexAttribPointer(attLocation[1] ,3, GL_FLOAT, 0, 0, attNormal);
cout << attLocation[1] << endl;
    
shader.end();
 
```

#結果
<img width="1020" alt="スクリーンショット 2018-01-20 13.51.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/184790/ea74b7b5-ccaa-d4be-5171-7ae258d70cc0.png">

はい、法線情報をattNormal(0.0, 0.0, 1.0)としていてそれをrgbに変換していたので、ちゃんと青として出ていますね.成功です.(やった!)

#つまずきやすい部分
自分がつまづいた部分ではあるんですが、今回のポイントは転送先のattribute変数が３次元ベクトルであるからと言って、転送元のattNormalは3次元ベクトルにしてはいけないってことなんですね。。。。float型の１次元配列で法線情報を保管しておいて、glVertexAttribPointer関数の第二引数でデータ要素数を指定すればいいってことでした.ふむふむって感じですねぇ。。。。
Laravel でテーブルが無いエラー

homestead 環境で Laravel プロジェクトを作成した後に、loginフォームをサブミットしたらエラーがでました。

# QueryExcetpion
エラーがでました。

```
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'homestead.users' doesn't exist (SQL: select * from `users` where `email` = pugiemonn@gmail.com limit 1)
```
`Base table or view not found` とあるので、テーブルが無いということだと思います。

<img width="50%" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/1624/8e448787-e94e-ea5b-3776-c4bedb16767e.png">

# migrate を実行
homesteadのboxにログインして、migrateコマンドを実行します。

```
php artisan migrate

Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table
```
テーブルを作成しました。

# 再度確認
テーブルがあるのでエラーがでなくなりました(☝ ՞ਊ ՞)
関係ないJavaScriptのエラーをSentryに送信させない

ちきさんです。

Sentryでエラー収集してますか？ してますよね。

さて、とあるWebアプリからSentryにエラーを送りたいが、下記の要件を満たす必要があったという話です。

*(Sentry JavaScript SDK => Raven)*

1. Ravenをバンドルしてはいけない。(極限まで容量を削減する必要があるため)
1. Sentryへのエラー送信がページ表示のパフォーマンスに影響を与えてはいけない。
1. 同じページ上で発生する関係ないJavaScriptのエラーをSentryに送信してはいけない。
1. 同じページに別のアプリのRavenが存在していたらそれを邪魔してはいけない。 

なかなか厳しい要件ですね。

これらを満たすためにやったことは、

- Ravenをバンドルせずにエラーを送信する必要が生じてから非同期でCDNから読み込む。
- 同じページに別のアプリのRavenが既に存在していたらCDNから読み込まない。(エラー収集を諦める)
- `window.Raven`を変数に格納し直してそれを使う。
- RavenがSentryにエラーを送信するために`window.onerror`を書き換えるので、エラー送信後に都度それを元に戻す。(`uninstall()`)
- エラー送信の度に`install()`と`uninstall()`をセットで行う。

以上です。
無理やり感があるので公式である程度柔軟にできるように対応していただきたいものです。

サンプルコードは↓

```ts:SentryService.ts
export class SentryService {
  private isRavenRequested = false
  private isRavenLoadedByThisApp = false
  private isRavenSetupByThisApp = false
  private myRaven: any = null;

  private loadRaven(): Promise<void> {
    if ((window as any).Raven && !this.myRaven) {
      return Promise.reject('Raven is already loaded by other apps.');
    } else if (this.myRaven) {
      return Promise.resolve();
    }
    return new Promise((resolve, reject) => {
      if (this.isRavenRequested) {
        resolve();
        return;
      }
      const ravenVersion = '3.22.1'
      const s = window.document.createElement('script')
      s.src = `https://cdn.ravenjs.com/${ravenVersion}/raven.min.js`
      s.async = true
      s.crossOrigin = 'anonymous'
      s.onload = () => {
        this.isRavenLoadedByThisApp = true
        if ((window as any).Raven && !(window as any).Raven.isSetup() && (window as any).Raven.VERSION === ravenVersion) {
          this.myRaven = (window as any).Raven; // window.Ravenを変数にコピーして、
          delete (window as any).Raven; // window.Ravenを削除する。
          this.isRavenSetupByThisApp = true;
        } else {
          reject('Raven is already setup by other apps.')
        }
        resolve();
      }
      s.onerror = reject
      window.document.body.appendChild(s)
      this.isRavenRequested = true
    })
  }

  captureException(e: Error): void {
    this.loadRaven()
      .then(() => {
        if (this.myRaven && this.isRavenLoadedByThisApp && this.isRavenSetupByThisApp) {
          console.log('captureException', e)
          try {
            this.myRaven
              .config('https://<key>@sentry.io/<project>')
              .clearContext()
              .setTagsContext({
                type: 'foo',
                account: '123',
              })
              .setExtraContext({
                log: ['do something', 'next something', 'finally something'],
                state: {
                  x: 'bar',
                  y: true
                }
              })
              .install() // window.onerrorハンドラを書き換える。
              .captureException(e) // Sentryにエラーを送る。
              .uninstall(); // window.onerrorハンドラを元に戻す。
            console.log('Sentryにエラーを送りました。')
          } catch (_) {
            this.myRaven.uninstall()
          }
        } else {
          console.log('SentrySDKがまだ読み込まれていません。')
        }
      })
      .catch(console.log)
  }
}
```
モーダルウィンドウの作り方 - Javascript

![on.gif](https://qiita-image-store.s3.amazonaws.com/0/135794/8b4df1b6-6352-bf6b-6543-b4c4698e9e41.gif)

# 実装

コピペで動くはず

```html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
#overlay {
    position: fixed;
    display: none;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0,0,0,0.5);
    z-index: 2;
    cursor: pointer;
}
</style>
</head>

<body>
<div id="overlay" onclick="off()"><h1>Hay</h1></div>
<button onclick="on()">Turn on overlay effect</button>

<script>
function on() {
    document.getElementById("overlay").style.display = "block";
}

function off() {
    document.getElementById("overlay").style.display = "none";
}
</script>  
</body>
</html> 
```
QNAPのシステムボリュームを暗号化しているときに，再起動すると管理画面にログインできなくなった場合の対処法

# TL;DR

SSHでログイン．SSHを無効にしている場合は，別の方法を探してください．

```shell-session
ssh admin@<server-name>.local
```

以下のコマンドを実行し，システムボリュームをアンロック，マウント．

```shell-session
mkdir /share/CE_CACHEDEV1_DATA
storage_util --encrypt_pwd pwd=YOUR_PW_HERE | sed 's/Encrypted passwd is://g' | cryptsetup -v luksOpen /dev/mapper/cachedev1 ce_cachedev1
mount /dev/mapper/ce_cachedev1 /share/CE_CACHEDEV1_DATA -t ext4
/etc/init.d/init_lvm.sh
```

管理画面用のHTTPサーバーを再起動．

```shell-session
/etc/init.d/Qthttpd.sh restart  # /etc/init.d/services.shでもいいかも
```

再起動時に毎回これをするのは面倒なので，安全性は犠牲になってしまいますが，システムボリュームは暗号化しないか，暗号化して鍵を保存してしまうのが一番簡単です．

教訓：必ずログインする方法を２つ以上用意すること

# 経緯

## ことの発端

QNAPを買いました．PCに保存されているデータのバックアップのためです．

以下の理由からすべてのボリュームを暗号化することにしました．

* 最初の起動時に，全くボリュームを作らなくても管理画面にログインできたので，システムボリュームがマウントされていなくても管理画面にはログインできると思い込んだ（これが不幸の始まり．．）
    * 管理画面からすべてのボリュームをアンロックできると思った
* 後から暗号化する場合，データをすべてコピーし直さないといけないので面倒（逆の場合も同じ）
* 暗号化してもそれほど遅くならないようだし，暗号化時でも十分な速度が出ているようだった

一通りデータをコピーした後，再起動したときに問題が発生した．．

## 管理画面にログインできない問題

再起動後，管理画面をブラウザで開くとエラーページにリダイレクトされ，ログイン画面が表示されません．理由は不明ですが，QNAPはいくつもHTTPサーバーを起動しているようなので（管理画面からWebサーバー機能などを無効にしても，なぜか起動し続けている），そちらのポートにも接続を試みて見ましたがだめでした．

問題発生時にメールを送信するように設定していたため，以下のような内容のメールが届きました．

> The default network shared folders (Public, Web, Multimedia, Download) are inaccessible. You must restore (or unlock) the system volume to a normal state. If the system volume is normal, the default network shared folders can also be restored in "Control Panel > Shared Folders".

安全のため暗号化鍵は保存しなかったから，当然起動時にシステムボリュームをマウントできません．それは予想通りだったのですが，初期起動時にはボリュームなしでも起動できたじゃないか．．

データをコピーする前に確認すべきだったと反省しても後の祭り．．すでに削除済みのデータのあったため，かなり衝撃を受けました．

## 助けてGoogle先生

よし，検索だ．
[Encryption - Can you enter the password via the command line - QNAP NAS Community Forum](https://forum.qnap.com/viewtopic.php?t=17988)

SSHでログインできればなんとかなるようです．

試してはいませんが，wgetの方法でも解決できるかもしれません．ただし，管理画面のUIが変わっていると動作しなくなると考え，storage_utilを使うことにしました．

# 補足

## HTTPサーバーポート

HTTPサーバーのポート設定の項目が幾つかありますが，これは変更しないことを強くお勧めします．

QNAPの管理画面から設定を変更した場合，設定ファイルが再生成される場合があります．この場合，以前設定した情報は上書きされ消えてしまいます．

例えば，`System port`を80に変更した後で（これを80に設定するのも結構面倒です），`Disable and hide multimedia functions`を有効にすると，`http://<server-name>.local`から管理画面にログインできなくなります．これは，`Disable and hide multimedia functions`を有効にするとWeb Serverの設定が再生成され，初期値の80番ポートに戻ってしまうためです．QNAPはWeb Serverを無効にしてもなぜかWeb Server用のHTTPサーバーを起動します．恐らく，Web Server用のHTTPサーバーは管理画面用のHTTPサーバーよりも先に起動するため，管理画面用のHTTPサーバーが80番ポートで起動できなくなるのだと推測されます．

## システムボリュームと管理画面へのログイン

後から調べて分かったことですが，`Disable and hide multimedia functions`を有効にすると，システムボリュームがマウントされていなくても管理画面にログインできるようになります．ただし，前述の点に注意が必要です．

## 暗号化鍵を外部USBメモリーに保存

試してはいませんが，起動スクリプトに手を加えることで，暗号化鍵を保存しなくても済むかもしれません．例えば，ボリュームのマウント時に外部USBメモリーに保存されているパスワードもしくは暗号化鍵を使用するとか．

類似の議論は2009年からあるみたいです．
[[Request] Use USB device for encryption key - QNAP NAS Community Forum](https://forum.qnap.com/viewtopic.php?t=14991)

# 参考

* [QNAPの基本構造について - uittie::blog](https://uittie.com/blog/2014/06/16/20140616qnap%E3%81%AE%E5%9F%BA%E6%9C%AC%E6%A7%8B%E9%80%A0%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/)
MTAについてまとめ

#MTAとは
>メール転送エージェント（英語：Mail Transfer Agent または Message Transfer Agent、略称：MTA）は、電子メールを相手方に送信するためのメールサーバ機能における中心的な機能である。
電子メールクライアント（MUA）からSMTPなどでメールを受信する。その後、相手別に振り分け、メール配送エージェント（MDA）へ振り分ける機能を持っている。
引用元：https://goo.gl/cKoEn6

一般的に使われているのは、「Sendmail」「qmail」「Postfix」の3つ
※「Exim」はDebianなどで標準採用

Sendmail→qmail→Postfix

メール送信に使用する
SMTP リレー: Gmail 以外の送信メールを Google 経由にする

#SMTP リレーとは？

>Microsoft® Exchange など、Gmail 以外の SMTP サービスを組織で使用している場合は、SMTP リレーサービスを設定して Google 経由でメールを送信することができます。SMTP リレーサービスの設定を使用すると、メールが外部の連絡先に配信される前に迷惑メールやウィルスをフィルタで除去したり、送信メールに G Suite のメール セキュリティ設定を適用したりすることができます。
https://support.google.com/a/answer/2956491?hl=ja

# 前提条件

`包括的なメール ストレージの設定`
Googleに関連するあらゆるメールをGmailで管理できる

`SMTPリレーサービスの送信制限`
・ユーザー当たり
1 日あたり：10,000 件、10,000人
・1 日に送信できるお客様あたりの合計の宛先数：G Suite アカウントのユーザー ライセンス数の約130倍

# ステップ1：SMTP リレーサービスを使用して送信メールの経路を指定する
>https://support.google.com/a/answer/2956491?hl=ja
必要なところを上記から抜粋

・`[許可する送信者]` 
>SMTP リレーサービス経由でのメール送信を許可するユーザーを選択

・`[認証]`
>・`指定した IP アドレスからのみメールを受信する`
指定した IP アドレスから送信されたメールのみを自社ドメインからのメールとして受け入れます。
→IPアドレスを指定
・`SMTP 認証を求める`
送信ドメインを特定するために SMTP 認証の使用を強制します。この方法を利用するには、クライアントを TLS 経由で接続する必要があります。

#ステップ 2: 社内導入型の送信サーバーで Google を指定する
>社内導入型の送信メールサーバーが smtp-relay.gmail.com を指すように設定します。

それぞれのサーバーの詳細な設定はURL参照
>https://support.google.com/a/answer/2956491?hl=ja
Nginx を利用して同一ポートの待ち受けで複数のプロトコルを扱う

転送先毎にサブドメインを用意しておき、TLS の SNI 拡張と Nginx の stream_ssl_module モジュールを利用して、SNI 拡張のドメイン名で転送先を切り替えます。

## Nginx

次の設定では、外からのアクセスは 443 ポートでのみ待ち受けていますが、www1.example.com で接続してきた場合は HTTP サーバ（8080）へ、www2.example.com で接続してきた場合は MQTT ブローカー（1883）へ転送しています。

### ngixn.conf

```
http {
    server {
        listen       127.0.0.1:8080;
        server_name  www1.example.com;

        location / {
            root html;
        }
    }
}

stream {
        upstream www1.example.com {
           server 127.0.0.1:8080;
        }
        upstream www2.example.com {
           server 127.0.0.1:1883;
        }
        server {
                listen 443 ssl;
                proxy_pass $ssl_server_name;

                ssl_certificate      /home/ubuntu/certs/server.pem;
                ssl_certificate_key  /home/ubuntu/certs/server.key;

                ssl_protocols TLSv1.2;
        }
}
```

### 証明書

SANs に www1.example.com と www2.example.com を指定した証明書を用意します。

```
$ openssl x509 -noout -text -in server.pem | grep -A1 'X509v3 Subject Alternative Name'
            X509v3 Subject Alternative Name:
                DNS:www1.example.com, DNS:www2.example.com
```

## 動作の確認

### HTTP (www1.example.com)

```
$ curl -s -D - -o /dev/null -X GET --tlsv1.2 --cacert ca.pem https://www1.example.com
HTTP/1.1 200 OK
Server: nginx/1.13.7
Date: Fri, 19 Jan 2018 17:13:29 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Fri, 19 Jan 2018 15:34:24 GMT
Connection: keep-alive
ETag: "5a621000-264"
Accept-Ranges: bytes

```

### MQTT (www2.example.com)

paho-mqtt で subscriber と publisher を用意して確認します。

- 最初に subscriber.py を実行しておき、別途 publisher.py を実行

```
(vs) $ python subscriber.py
b'Hello, World'
```

- subscriber

```
import ssl
import paho.mqtt.client as mqtt

def on_message(client, userdata, msg):
    print(str(msg.payload))

client = mqtt.Client()
client.tls_set("ca.pem", cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLSv1_2)
client.on_message = on_message
client.connect("www2.example.com", 443, 60)
client.subscribe("a/b")

client.loop_forever()
```

- publisher

```
import ssl
import paho.mqtt.client as mqtt

client = mqtt.Client()
client.tls_set("ca.pem", cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLSv1_2)
client.connect("www2.example.com", 443, 60)
client.publish("a/b", "Hello, World")
```
FFmpegで現在時刻を表示させながら出力する

# 概要
タイトルの通り、FFmpegでエンコード、出力する際に現在時刻を表示させながら出力する。rtmp形式で出力させたりするときとかにそのときの時刻がわかって便利。

# やり方
パラメータに以下を含める（filter_complexについてはvfとかでもいけるはず）。
`-filter_complex "drawtext=fontfile=<フォントファイルのパス>:text='%{localtime\:%X}'"`

localtimeについては以下を参照。
https://ffmpeg.org/ffmpeg-filters.html#Text-expansion

`%X`で現在の時刻が出力される。仕様では`see strftime(3)`とあるので、それ以外の値（日付とか）を出力させたい場合は以下が参考になる……かもしれない（未確認）。
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/strftime.3.html
Linuxからメールを送る

# メールを送るコマンド

```linux-config
echo "本文" | mail -s "タイトル"　-r "送り主メールアドレス" "送り先メールアドレス"
```

`-s`オプション：タイトル指定
`-r`オプション：送り主メールアドレス指定
コマンドの最後に`送り先メールアドレス`を記載
OpenCVで読み込んだ画像をChainer用に変換し、それを再びOpenCV用に変換するメモ

深層学習において、分類問題でなく画像を出力とした場合を想定している。画像を出力にした場合にはそのままではOpenCVで可視化できない。

# 動作環境

- Ubuntu 16.04
- Python 3.5.2
- Chainer 3.2
- numpy 1.13.3
- opencv-python 3.2

# 画像をChainerの入力にするために変換する

OpenCVで画像を読み込むと縦、横、色数の順で構成された行列となっているが、Chainerで利用する時は色数、縦、横に変換する必要がある。さらに形式をuint8からfloat32にする必要がある。

```python3
def img2arr(imgs, norm=255, dtype=np.float32, gpu=-1):
    """
    入力画像リストをChainerで利用するために変換する
    [in]  imgs:  入力画像リスト
    [in]  norm:  正規化する値（255であれば、0-255を0-1に正規化する）
    [in]  dtype: 変換するデータタイプ
    [in]  gpu:   GPUを使用する場合はGPUIDを入力する
    [out] 生成された行列
    """

    shape = imgs[0].shape
    w, h = shape[:2]
    if(len(shape) == 2):
        ch = 1
    else:
        ch = shape[2]

    if(gpu >= 0):
        return xp.array(imgs, dtype=dtype).reshape((-1, ch, w, h)) / norm
    else:
        return np.array(imgs, dtype=dtype).reshape((-1, ch, w, h)) / norm
```


# Chainerの出力を表示するために変換する

先ほどとやっていることは同じだが、dtypeをuint8にするタイミングに注意が必要。1行で同時に処理しようとすると0と255のみで構成される画像として変換されてしまうため注意が必要。

```python3
def arr2img(arr, ch, size, norm=255, dtype=np.uint8):
    """
    Chainerの出力をOpenCVで可視化するために変換する
    [in]  arr:   Chainerから出力された行列
    [in]  ch:    画像に変換する際のチャンネル数
    [in]  size:  画像に変換する際の画像サイズ
    [in]  norm:  正規化をもとに戻す数（255であれば、0-1を0-255に変換する）
    [in]  dtype: 変換するデータタイプ
    [out] OpenCV形式の画像に変換された行列
    """

    y = np.array(arr).reshape((-1, size, size, ch)) * 255
    return np.array(y, dtype=np.uint8)
```

もっといい方法があればコメントください。
Homebrewでインストールした複数バージョンのphpを切り替える (Deprecated)

動作確認はMacにphpをインストールしてローカルで行っています。
しかし、本番環境のphpバージョンと同じバージョンで動かしたいこともあるので、複数のphpバージョンを切り替えたいときがあります。
Homebrewを使う、phpのバージョン切り替え方法です。
コマンドラインで動作するphpとWebサーバー（Apache）で動作するphpのバージョンを変更します。

# まずは複数バージョンのphpをインストール

違うバージョンのphpをインストールする前に、**今動いているバージョンのphpをunlink**します。
※下記の記述は古いバージョンです。

```
brew install php56 php56-apcu php56-imagick php56-intl php56-mcrypt php56-opcache php56-xdebug --with-httpd24 --with-libmysql

brew unlink php56

brew install php71 php71-apcu php71-imagick php71-intl php71-mcrypt php71-opcache php71-xdebug
brew install php71 --with-httpd24
```

# 切替方法

## brewコマンドで対象のphpバージョンを変更

今動いているバージョンのphpをunlinkして、動かしたいバージョンもphpをlinkします。
例えばphp7.2を動かしていて、php5.6にしたい場合は

```
brew unlink php
brew link php@56
```

## Apache httpd.confを修正

※httpd.confやlibphp*.soの場所は環境によって違います。

```/usr/local/etc/httpd/httpd.conf
LoadModule php5_module /usr/local/Cellar/php@5.6/5.6.35/lib/httpd/modules/libphp5.so
# LoadModule php7_module /usr/local/Cellar/php/7.2.4/lib/httpd/modules/libphp7.so
```

あとはApacheを再起動すれば反映されます。

```
# Macの場合
sudo apachectl restart
```
Visual Studio 2017でUnity開発用の拡張機能を有効にする

# 概要
UnityでVisual Studio 2017を使うつもりが，なぜだかUnity用の拡張機能が有効になってなかった(多分セットアップ中の設定の漏れ)ので，既にVisual Studio 2017が入っている環境で有効にしてみました。

# 方法

## セットアップファイルを落としてくる

[Visual Studio のダウンロード](https://www.visualstudio.com/ja/downloads/) からVS2017を落としてきます。
落としてきたセットアップファイルを実行して，すでにインストールされているVisual Studio 2017の変更を行います。

![vs_unity_1.PNG](https://qiita-image-store.s3.amazonaws.com/0/102294/b6bd07cf-7923-295b-7d97-c9f90817c946.png)

## "Unityによるゲーム開発"にチェックを入れる

"モバイル&ゲーム"の"Unityによるゲーム開発"にチェックを入れて"変更"をすれば有効になります。

![vs_unity_3.PNG](https://qiita-image-store.s3.amazonaws.com/0/102294/270a9ca1-e0eb-4af5-425e-6d2fd5c1ca0a.png)

# 余談
以前VS2015で使ってたときなんかだと[Visual Studio Tools for Unity](https://msdn.microsoft.com/ja-jp/library/dn940025.aspx)を使ってたので，2017向けも何処かから取ってくるものかと思ってました。
C# にて「ジャバ == ノットジャバ」を真にする

下記の記事を見まして、形容しがたい感情をおぼえたため、 [自作キーボードのキーら](https://twitter.com/toby_net/status/953617939927740416)を断続的に押下することにいたしました。

> [Can (a ==1 && a== 2 && a==3) ever evaluate to true?](https://stackoverflow.com/questions/48270127/can-a-1-a-2-a-3-ever-evaluate-to-true)
> [JavaScriptで「(a ==1 && a== 2 && a==3)」の結果を真にする手法が議論される | スラド デベロッパー](https://developers.srad.jp/story/18/01/18/0648254/)
というのがちょっとした話題らしい。
> 
> ならば、ジャバorノットジャバも真にできるのでは！と思って作ってみまし
> た。

ref. [Rubyでジャバandノットジャバを真にする - Qiita](https://qiita.com/asny/items/ce8a275511c0c6d86ade)

処理系および環境は `Visual Studio 2017` 付属の `C# Interactive` ウインドウから。
まずは、以下のコードをご覧ください。

```shell
Microsoft (R) Roslyn C# コンパイラ バージョン 2.6.0.62329
'CSharpInteractive.rsp' からコンテキストを読み込んでいます。
詳細については、「#help」と入力します。

> bool? ジャバ;
> bool? ノットジャバ = !ジャバ;
> ジャバ == ノットジャバ
true
```

ご覧の通り、定義によっては、以下が成り立ちます。

> ジャバ == ノットジャバ
> true

意図しない挙動に見えるかもしれません。

しかしながら、技術やツールというものがどう活かされるか、人を幸せにするか、人類滅亡の引き金となるか、ジャバするか、それは人次第というものです。使いようによって、 人は「`ジャバ == ノットジャバ` を **真** にする」ことができる。このことを念頭におき、日々、我々は今すぐダ…… 失敬、プログラミングしたいものです。
Docker ライフサイクル 概要図 ＆ 環境複製手順


先日初めてDockerに触れ、環境を複製する際に色々調べたのでその内容を共有するため図を作りました。

![Docker概要図.png](https://qiita-image-store.s3.amazonaws.com/0/89911/3c1a5f8e-6a47-23be-8939-ec1e0cde3aa6.png)

オプションなど含めた実際利用するコマンド一覧については@voluntasさんの下記の記事がおすすめです。

[docker コマンド チートシート
](https://qiita.com/voluntas/items/68c1fd04dd3d507d4083)

## 環境複製の手順

環境を1つのサーバーから別サーバーにまるっと複製する場合は、下記手順でうまくいきました。

### 作業したコンテナから`commit`でDockerイメージを作成

```bash
$ sudo docker commit <your_container> <your_new_image>
```

### Dockerイメージを`save`でtarファイル化し新環境にコピー

```bash
$ sudo docker save -o image_backup.tar <your_new_image>
```

### 新環境でtarファイル化されたDockerイメージを`load`しDockerイメージ化

```bash
$ sudo load -i image_backup.tar
```

### そのDockerイメージから`run`か`create`でContainer生成

```bash
$ sudo docker run -it --name <new_container> -p 8000:8000 <your_new_image>
```

### ボリュームを使ってる場合旧環境で`*1`コマンドを実行しボリュームをtarファイル化

```bash
$ sudo docker run --rm --volumes-from <old_container> -v $(pwd):/backup busybox tar cvf /backup/backup.tar <volume_path>
```

### 新環境でtarファイル化したボリュームから`*2`コマンドでリストア

```bash
$ sudo docker stop <new_container>
<new_container>
$ sudo docker run --rm --volumes-from <new_container> -v $(pwd):/backup busybox tar xvf /backup/backup.tar
```

DockerコンテナとDockerボリュームの関係性でつまづきました。コンテナを`commit`すればすべてバックアップされるものかと思っていましたが、そうではなくボリュームでマウントされた保存領域はホストOS内で指定したディレクトリで別途バックアップ＆リストアが必要な点でした。そしてVolumeをバックアップ＆リストアするためのコマンドが特に用意されていない……

そのうちDockerfileやDocker Composeについても調べてまとめたいな。
GKEでロードバランサのIPを指定するとき、グローバルに属するIPアドレスリソースは使用できないので注意

GKEでKubernetesを使用する時、よく`type: LoadBalancer`を使用すると思います。
以下のように`loadBalancerIP`を使用する場合、注意が必要です。

```service.yaml
apiVersion: v1
kind: Service
metadata:
  name: hoge
  labels:
    app: hoge
spec:
  ports:
    - port: 80
  selector:
    app: hoge
    tier: frontend
    environment : stage
  type: LoadBalancer
  loadBalancerIP: xxx.xxx.xxx.xxx
```

`xxx.xxx.xxx.xxx`は事前に取得した静的アドレスである必要がありますが、静的アドレスのタイプが「グローバル」の場合は以下のエラーが出力されます。

```
Error creating load balancer (will retry): Failed to create load balancer for service default/eccube: requested ip xxx.xxx.xxx.xxx is neither static nor assigned to LB xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx(default/hoge)
```

リージョン指定するタイプの静的IPを確保しましょう。
なお、Serviceで作成されるTCPロードバランサではなく、Ingressで作成されるHTTP(S)ロードバランサはグローバルに属するIPアドレスが使用できます。
不均衡データに対するClassification その２

#はじめに

　以前に不均衡データにおいて少数派データの識別精度の劣化を抑制する方法として、オーバーサンプリング・アンダーサンプリング・コスト関数のウェイト調整の実装方法とその効果について報告しました。
https://qiita.com/ryouta0506/items/619d9ac0d80f8c0aed92

今回はその続きということで、kaggleの「Credit Card Fraud Detection」について教師付き異常値検知問題としてアプローチした結果を報告したいと思います。

#異常値検出アプローチ概略

　教師付き異常値検知は、クラス1とクラス2の確率密度関数を決定することで学習を終わらせることができます。この確率密度関数に必要なパラメータは、ロジスティック回帰やSVMのように探索的アプローチと異なり、解析的アプローチによって得ることができます。そのため、ロジスティック回帰やSVMのようなコスト関数が必要とされません。つまり、クラス間の件数の割合と無関係にパラメータが決定できることになります。
　今回の場合、特徴量にV1～V28と多変量を扱うため、多変量確率密度関数を取り扱うことになります。したがって、計算の対象は平均値ベクトルと共分散行列です。平均値ベクトルは、確率密度関数の算出する期待値が最大になる変量の組み合わせのようなものです。最も単純な多変量確率密度関数である2次元の場合、その関数の形状は立体の山のようになります。平均値ベクトルはこの山の頂上の位置ということになります。共分散行列は2つの情報が含まれた行列です。ひとつは、変量の分散です。先の山のたとえでいうと裾野の広がりにあたります。ふたつめは、変数間の相関です。X軸方向の分散が2、Y軸方向の分散が1、のような場合、上から見た裾野は楕円形になります。その際、楕円が右上がりなのか左上がりなのか、さらにはX軸に対して並行なのかを決定するためにこの相関があります。

　クラス1とクラス2の確率密度関数が定義できたら、未知のデータに対して各クラスに属する期待度を計算し、結果を比較することで識別を行います。単純に比較すると両者の大小で実現できますが、教科書通りにいうと、以下の数式で計算した結果を閾値と比較して識別します。

　log(クラス1の期待値 ÷ クラス0の期待値) = log(クラス1の期待値) - log(クラス0の期待値)

# 実装

実装のコードを以下に示します。

```python3
from sklearn.model_selection import train_test_split   
from scipy.stats import norm
import scipy.stats as sc
from sklearn import metrics

nega_train , nega_test = train_test_split(dat01_nega)
posi_train , posi_test = train_test_split(dat01_posi)

test = pd.concat([nega_test,posi_test])

collist = list(test.columns.values)
collist.remove('Time')
collist.remove('Amount')
collist.remove('Class')

# 平均ベクトルの算出
mean_posi = posi_train[collist].mean()
mean_nega = nega_train[collist].mean()
# 共分散行列の算出
cov_posi  = np.cov(posi_train[collist].as_matrix().T)
cov_nega  = np.cov(nega_train[collist].as_matrix().T)

# クラス1とクラス2の期待値の算出
tmp = test[collist].as_matrix()

pdf_posi = sc.multivariate_normal.pdf(tmp, mean=mean_posi , cov=cov_posi)
pdf_nega = sc.multivariate_normal.pdf(tmp, mean=mean_nega , cov=cov_nega)

# 識別
C1 = 0.0000000001        # 結果がゼロになった場合に備えた補正値
C2 = 0.0000000001

result_list1 = np.log2(pdf_posi + C1) - np.log2(pdf_nega + C2)
result_list2 = [1 if x>=0 else 0 for x in result_list1]
```
　なお、期待値が極小の場合、ゼロ扱いとなるためlog2の結果がinfとなってしまいます。そこで、補正をすることにしました。補正には乗算と加算がありますが、確実に結果が得られるように加算にしています。この値によって識別結果に影響することが予想されるので、問題に応じて調整することが求められます。
　識別の閾値は無難にゼロとしました。この値も問題に応じて調整すべきものでしょう。

# 結果

 識別結果は、以下のようになりました。

|Predict|    0|    1|All|
|:-----------------|------------------:|------------------:|---------------:|
|Class  |     |     | |
|0      | 67633|    3446|71079|
|1      |   12|  111|123|
|All    |67645|3557|71202|

全体の識別率は95.14％とまずまずです。また、クラス1の識別率も90.24％（111÷123）と相応の結果となりました。前回のオーバーサンプリング時のものとおおよそ同等の性能となりそうです。

# まとめ

　あえて複雑なモデルベースの手法を用いなくともシンプルなものでもそれなりの成果が得られることが確認できました。本問題の場合、V1～V28の特徴量がうまく正規分布に近かったことが影響しているのかもしれません。この結果からするとベイズ識別器を使えばさらにいい精度が得られることも期待できるのではないでしょうか。
【備忘録】Laravelでアップロードした画像ファイル名の取得。

#**アップロードした画像名の取得**

$imageName1 = $request->file('image_1')->getClientOriginalName(). '.' . $request->file('image_1')->getClientOriginalExtension();

$request->file('image_1')->getClientOriginalName()・・・ファイル名取得
getClientOriginalExtension()・・・ファイル拡張子取得
nginx unit 0.3でvirtualenvを試す

nginx unit 0.3 beta リリース
===
https://www.nginx.com/blog/unit-0-3-beta-release-available-now/
去年末にnginx unitの0.3がリリースされていた。

virtualenvに対応したということだったので、ちょっと試してみた。
unit-pythonの3が入らなかったのでとりあえずpython2.7.5で。(気が向いたらビルドからやってみる)

環境
===
- vagrant(Centos7.4)
- openresty/1.13.6.1()
- unit 0.3
- python2.7.5


準備
===
## python
```
$ virtualenv test
$ source bin/activate
$ pip install routes
```
```
$ cat app/index.py 
from routes import Mapper
import json

def application(environ, response):

    map = Mapper()
    map.connect(None, '/error/{action}/{id}', controller='error')
    map.connect('home', '/', controller='main', action='index')

    result = map.match(environ['REQUEST_URI']);

    response('200 OK', [
        ('Content-type', 'text/plain')])

    return [json.dumps(result)]
```

## unit
あらたに追加されたhomeにvirtualenvのパスを書いてやると良いらしい。

```
$ cat /etc/unit/unit.conf 
{
    "listeners": {
        "*:8000": {
            "application": "test"
        }
    },
    "applications": {
        "test": {
            "type": "python",
            "workers": 4,
            "working_directory": "/var/www/test",
            "path": "app",
            "home": ".",
            "module": "index"
        }
    }
}
```

## nginx
ほとんどデフォルトのままなのでいるとこだけ。

```
    upstream unit-python {
        server 127.0.0.1:8000;
    }

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location = /favicon.ico {
            empty_gif;
        }

        location / {
            proxy_pass http://unit-python/;
            proxy_set_header Host $host;
        }

        #error_page  404              /404.html;

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
```

起動
===
```
sudo systemctl start nginx
sudo systemctl start unit
sudo curl -X PUT -d @/etc/unit/unit.conf --unix-socket /var/run/control.unit.sock http://localhost/
```

![スクリーンショット 2018-01-20 12.28.40.png](https://qiita-image-store.s3.amazonaws.com/0/75617/95ab181a-341d-68a9-376e-46ca100b2597.png)

いけた。