OmniAICreator/Qiita-1.07M huggingface dataset:
Railsプロジェクトにdeviseを使ってログイン機能を導入する

# はじめに
Railsで作ったサービスに簡単にログイン認証機能を導入できるdeviseの使い方を簡単に解説します。（説明とかいいからログイン認証機能さっさと導入したいという方のお役にも立てると思います。）

# deviseとは
deviseはRailsで作ったアプリに簡単にログイン認証機能を導入できるgemです。
（ログインしていない人にはこのページみせない、、とかログイン何回かミスったらアカウントをロックしたりとかの機能導入できます。）

# deviseを導入する

まずRailsプロジェクトを作成していきます。

``` 
$ rails new devise-sample-app
$ cd devise-sample-app
```

Gemfileに以下を記述してインストールします。

``` Gemfile:Gemfile
# devise
gem 'devise'
```

```terminal:terminal
$ bundle install
```

次に下記コマンドを実行します。

```terminal:terminal
$ rails g devise:install
```

するとdeviseのセットアップ方法が表示されます。

```terminal:terminal

Some setup you must do manually if you haven't yet:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

===============================================================================
```
簡単に説明すると、

1. 本登録メールに記載されるURLの指定を行ってください。
2. route.rbでrootのURLの設定をしてください。
3. エラーメッセージを表示するHTMLをapplication.html.erbに追加してください。
4. deviseで生成されるデフォルトのログイン画面などをカスタマイズしたい場合、下記のコマンドを実行してください。

のような意味ですが今ここでは設定しなくて大丈夫です。

次に下記コマンドを実行します。

```terminal:terminal
$ rails g devise User
```
上記コマンド実行後に

```terminal:terminal
$ rails db:migrate
```

で、「localhost:3000/users/sign_up」にアクセスすると会員登録画面ができています。

![スクリーンショット 2018-05-22 5.45.14.png](https://qiita-image-store.s3.amazonaws.com/0/195724/43bf5f6f-bccc-3e3a-9e72-ec60178635a3.png)

# ログイン認証を設定する
これでログイン機能ができたので、次はログインが必要なページとログインなしで閲覧できるページを作成します。

```terminal:terminal
$ rails g controller home index mypage
```

上記コマンドでhomeコントローラーとログインが必要なmypage、ログイン不要なindexを作成します。

次にhome_controller.rbにbefore_actionを追記します。
![スクリーンショット 2018-05-22 5.56.16.png](https://qiita-image-store.s3.amazonaws.com/0/195724/08aac091-a9ff-c911-e781-23c0868b4ee1.png)

これでmypageにアクセスするとログインを要求され、indexにアクセスするとそのまま閲覧できるかと思います。
IAMの最小権限を調整するために、Amazon Macieを活用する

こんにちは、ひろかずです

[IAMのベストプラクティス](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html)には、`最小権限を付与する`という項目があります。
最小権限の確認には、遮断されたAPIを確認したいところです。

お手軽な確認方法としてMacieが使えそうなことに気がついたので、一筆書きます。

# ざっくり構成
今回は、デフォルト暗号化を`KMS暗号化`に設定したs3バケットにオブジェクトをPutするというシチュエーションで試してみました。
ぱっと見で、当該s3に対するPutObjectがあれば良さそうですよね。
KMSへのアクセス権が必要そうですか？
何をつければ良いのでしょう？

![figure1.png](https://qiita-image-store.s3.amazonaws.com/0/46611/f3dcd87a-0060-5604-b2d0-0d61a364170f.png "figure1.png")

# まずやってみる
AccessDeniedしてしまいました。

```
$ aws s3 cp test.txt s3://bucket/ --profile readonly
upload failed: ./file.txt to s3://bucket/test.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
```

権限を見てみます。
readonlyユーザーにs3putの権限を追加しています。

![iam1.png](https://qiita-image-store.s3.amazonaws.com/0/46611/d749cb89-d7ec-7409-7749-1fafedddea3c.png "iam1.png")

Limited-s3-put-secretの設定は以下です。

![iam2.png](https://qiita-image-store.s3.amazonaws.com/0/46611/4bd8f8cd-78b8-1b87-6305-fad7ccf40000.png "iam2.png")

# MacieでAccessDeniedされたAPIアクセスを確認する
RESEARCH画面で以下検索クエリーを実行します。

- コマンド実行から検索可能になるまで肌感で5-10分程度のタイムラグがあります。
- 自分で試す時は、xxxxx...の部分はAWSアカウントIDに置き換えてください。
- 自分で試す時は、readonlyの部分を使用しているIAMユーザー名に置き換えてください。

```
eventNameErrorCode.secondary:"AccessDenied" AND @timestamp:[now-1d TO now]  AND  macieUniqueId:"xxxxxxxxxxxx\:user\:readonly"
```

スクロールしていくと、検索結果が表示されます。
PutObjectの失敗に混じって、`GenerateDataKey:AccessDenied`がありますね。

![macie2.png](https://qiita-image-store.s3.amazonaws.com/0/46611/da4e338d-9ed3-2992-45a4-18ae793e3314.png "macie2.png")

展開するとこんな感じで表示されます

![macie3.png](https://qiita-image-store.s3.amazonaws.com/0/46611/698d4244-0e75-05a6-f0be-d4fcfb344d02.png "macie3.png")

# DenyされているAPIを許可する
GenerateDataKeyを許可するIAMポリシー`Limited-kms-generate_datakey`を作成してアタッチしました。
![iam3.png](https://qiita-image-store.s3.amazonaws.com/0/46611/4678f6c7-ec08-d0fe-d6e5-18c185a2bc06.png "iam3.png")

`Limited-kms-generate_datakey`の設定は以下です。

![iam4.png](https://qiita-image-store.s3.amazonaws.com/0/46611/c7e28976-e5ec-dcfe-313a-d2e860e7b13f.png "iam4.png")

# もう一度やってみる
成功しました。

```
$ aws s3 cp test.txt s3://bucket/ --profile readonly
upload: ./test.txt to s3://bucket/test.txt
```

# 最後に
これまで、失敗したAPIコールの調査ではCloudTrailを確認する他ありませんでしたが、検索可能にする作り込みの手間や、Athenaの課金が心配な状況でした。
Macieを使うことで、直感的に失敗したAPIコールを調査することができるようになったと思えます。
今後も活用していきたいですね。

今日はここまでです。
お疲れ様でした。
TensorFlowインストール[On Windows 10 + CUDA 9.0 + cuDNN 7.0.5 + Anaconda 5.1]

Windows 10にTensorflowの環境を構築しています。
GPU利用環境ですので、CUDA及びcuDNNはインストール済みです。
以下、関連記事となります。

- [Windows 10でCUDA 9.0がインストールできない（Visual Studio Integrationで失敗する）場合の対処方法](https://qiita.com/spiderx_jp/items/cccb2fb35fdaced022ce)
- [cuDNN v7.0.5 for CUDA 9.0のインストール[On Windows 10]](https://qiita.com/spiderx_jp/items/5f733c88e7437bc5e2d4)

#前提条件
- Windows 10 Pro Version 1803
- Visual Studio Community 2015 Update 3
- NVIDIA GeForce GTX 1080 Ti
- CUDA 9.0
- cuDNN 7.0.5

#Anacondaのインストール
[TensorFlowの説明](https://www.tensorflow.org/install/install_windows)では、Anacondaは公式サポートしていないようですが、Ubuntuにインストールした際、いろいろとバージョン依存関係で苦戦したので、Anacondaをインストールすることにしました。
[Anacondaのダウンロードサイト](https://www.anaconda.com/download/)から、 Python 3.6 versionの最新(Anaconda3-5.1.0-Windows-x86_64.exe)をダウンロードしてきます。
※私は64bit版にしました。

ダウンロードが完了したら、インストールします。
Visual Studio Codeもインストーラに付いていたので、ついでにインストールしてみました。

#TensorFlowのインストール
[TensorFlowサイト](https://www.tensorflow.org/install/install_windows)の「Installing with Anaconda」に従って、インストールしていきました。

##conda仮想環境を作成
Anaconda 5.1.0では、Python 3.6ですが、TensorFlow公式サイトに従い、3.5としました。

```Console:AnaondaPrompt
$ conda create -n tensorflow-py35 python=3.5
```

##TensorFlow-GPUのインストール
先ほど作成した「tensorflow-py35」というconda仮想環境にTensorFlowのGPU版をインストールします。

```Console:AnaondaPrompt
$ conda activate tensorflow-py35
```

```Console:AnaondaPrompt
$ pip install tensorflow-gpu
```

#動作確認
動作確認のため、kerasをインストールして、MNISTのサンプルを動かしてみます。
※MNISTの解説は、[こちら](http://tensorflow.classcat.com/2016/03/09/tensorflow-cc-mnist-for-ml-beginners/)が良いです。

まずは、Kerasをインストールします。

```Console:AnaondaPrompt
$ pip install keras
```

[mnist_cnn.pyをダウンロード](https://github.com/NVIDIA/keras/blob/master/examples/mnist_cnn.py)してきます。

以下のコマンドでMNISTを実行します。

```Console:AnaondaPrompt
$ python mnist_cnn.py
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100712/8a923cf6-e1b2-a24c-9575-271d04565272.png)

約1分程度で実行完了しました。

以下、実行時のGPU状況です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/100712/d0ef9c11-8d3c-d86f-b623-35d4d6e76e37.png)

#CPU版だとどうなる？
ちなみに、CPUで実行したらどうなのか？と思い、tensorflow CPU版用のconda仮想環境を新規作成して実行してみました。

GPU版では、「tensorflow-gpu」でしたが、CPU版は「tensorflow」と指定します。

```Console:AnaondaPrompt
$ pip install tensorflow
```


以下、実行結果です。実行完了までに約15分程度、GPU版の約15倍でした。
全然違いますね！
ちなみに、CPUは「i7-7700K」です。
タスクマネージャーで見ると100%稼働となっていました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100712/b40e1406-3123-e543-3dd1-4bead82c5a60.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/100712/85bd6dbc-2c1a-5588-28f4-75786d72cbc1.png)

以上
macOS high SierraでJw_cadを使う手順まとめ

# はじめに

必要に迫られて使おうと思ったら、起動しなかったり、文字化けするなどして困ったので、解決までの手順をまとめます。

# ダウンロード

以下のURLにてダウンロードできます

[JW-CAD for Mac](https://jwcad.keinos.com)

ダウンロードしたファイルを解凍して、「JW-CAD.8.02b_for_Mac」をアプリケーションフォルダにコピー。

# 起動

アイコンを右クリックして「開く」を選択し、起動しないことを確認...(^_^;)

次に、「パッケージの内容を表示」を選択し、中にある「Wineskin」を起動。

![スクリーンショット 2018-05-22 2.40.53.png](https://qiita-image-store.s3.amazonaws.com/0/114240/97571042-4e62-3922-6d04-1497dbc21f3f.png)

「Set Screen Options」をクリックし、以下のとおり設定。

![スクリーンショット 2018-05-22 2.41.00.png](https://qiita-image-store.s3.amazonaws.com/0/114240/28d2fd20-edd1-de83-abd3-e062fa549b0e.png)

ここで、Jw_cadを一度起動し、動作することを確認。ただし、メニューなどが文字化けしているはずです。

# 日本語入力

Jw_cadを終了してから、再度「Set Screen Options」を表示させ、以下のとおり設定する。

![スクリーンショット 2018-05-22 2.41.05.png](https://qiita-image-store.s3.amazonaws.com/0/114240/df75a189-4230-0233-2a12-a0d91a6e9f8f.png)

設定後、Jw_cadを起動して日本語が入力できることを確認。

# 文字化け対策

「Wineskin」を起動し、「Advanced」をクリック。

![スクリーンショット 2018-05-22 2.41.17.png](https://qiita-image-store.s3.amazonaws.com/0/114240/d1a75187-51bf-dbc6-949d-2643936b9b11.png)

ここで、「Winetricks」を起動。

![スクリーンショット 2018-05-22 2.42.00.png](https://qiita-image-store.s3.amazonaws.com/0/114240/3d1ecd4c-5009-9d46-a68c-bf451d9115e2.png)

「fonts」を展開し、「cjkfonts」にチェックを入れてから「Run」をクリック。

![スクリーンショット 2018-05-22 3.14.03.png](https://qiita-image-store.s3.amazonaws.com/0/114240/0a1cfc7f-75e8-7b3c-2727-3c2813a4fa15.png)

しばらく待つと終了するので、「Close」をクリックして終了。

# 動作確認

再度Jw_cadを起動すると、文字化けが解消されている...はず。

![スクリーンショット 2018-05-22 3.47.03.png](https://qiita-image-store.s3.amazonaws.com/0/114240/85b73fef-f30e-666a-2fa8-21758d24615c.png)

メニューフォントが見えにくい場合は、winecfgを起動して「Desktop Integration」の「Menu Text」のフォントを「ヒラギノ角ゴシック W4」、サイズ「9」に設定すると、少し見やすく表示されるようになるはずです。

とりあえず、できた。
僕はただ Hello world がしたかっただけなんだ

![IMG_6732.jpg](https://qiita-image-store.s3.amazonaws.com/0/149269/6fce8039-5d42-3658-b682-9b42240ea398.jpeg)

# Summary

この記事はたんなるポエム

たんなる事務員さんがプログラミングをした話

`Hello World`するなら最初は`IDE`がお得

テキストエディタはまさに人生を行くが如し

## きっかけ

そう短くはない期間、営業職・事務職としてお仕事してたんですけど、まぁ辞めようかなと決めたのが３年前の秋口。で退職してひまひま〜だったので、暇つぶしにプログラミングしようかなと。

## なんとなく関数型言語

たしか当時、今書いてるブログサイト`Qiita`で、関数型言語とは？みたいな論戦が`なんとか壁`さんを中心にあって、それで関数型言語やってみようかな〜と。

事務処理で`VBA`というのを触ったことがあったので、同じマイクロソフトという会社が作ってる`F#`というのがあったので、それをやってみようかなと。


## コードを書いてみたのよ

`Mac`持ってるんですけど`Visual Studio`を`VM`上の`Windows8.1`にインストールして使ってみようと思ったんですけど、ものすごく複雑すぎて、僕にはむり〜となってすぐ消したんですよ。

たぶんテキストエディタの方が簡単だろうな思って`Vim`というのが最初からパソコンにあったので、これでやろうとかなと。

とりあえず四苦八苦しながら下記のコードを書いて

```fsharp
Printfn "Hello World"
```

`Vim`のコマンドで`:QuickRun`としたら

```
Hello World
```

おおお。でた！


## ここからが大変だった

`Hello World`を表示させるのに３秒ほどかかるとか、単語の補完するのに結構な頻度でエラーになったりとか、どうやったらテストコードを書けるのかとか、デバッグをどうやったらいいのか、とかとか。


## 意地になって環境改善してみたら

`Vim`で`F#`のコードを実行、テスト、デバッグさせるのに、気づいたら２年が経過してたぽよ、、、。というか、例えばシャワーを浴びてる時でさえどうやって改善しようかとか`vim`と`F#`のことずっと考えてましたし。

## 感想（あくまでも個人の感想です）

テキストエディタはやさしいというのは嘘。

テキストエディタにはいろいろなものがあるみたいですが、特に`Vim`というテキストエディタはスモーキー。（たったった　たったった　ほわ〜ん）

実際のところ関数型言語も`VBA`もそんなには変わらない。

`Python`がオススメな気がする。

`Sler, SE, Programmer`な人たちに質問すると最初は優しいけど、徐々に厳しくなってきて、上達の気（け）が見えてこないと叱咤する傾向がある。

`IDE`があるならそっちを使うのがいいかも〜。でも、テキストエディタで`Hello World` することは色々な意味で鍛えられる。

## Thanks

当たり前ですが、色々な方のヘルプがあったおかげで`F#'s Hello World`ができました。本当に本当にありがとうございます！

```text
// special thanks:
// twitter account

@ShougoMatsu 
@mattn_jp 
@thinca 

@yukitos 
@bleis 
@htid46 
@omanuke 
@haxe 
@kekyo2 
@pocketberserker
```
supervisorで設定ファイルをincludeしているつもりなのにincludeされない場合に確認すること


supervisorで、別で定義した設定ファイルから設定をincludeさせる場合があるかと思います。

```/etc/supervisord.conf
[include]
files = /etc/supervisor/conf.d/*.conf
```

というような記述を追加して
supervisordを再起動しても設定が反映されないときは
supervisorのバージョンが2系であることが原因である可能性が高いです。

2系だと `supervisorctl version` というコマンドがありません。
なければ2系と考えてよいです。

2系の場合`[include]` セクションで別ファイルを読み込むということができないので、
/etc/supervisord.confに記載するか3系にアップグレードさせる必要があります。

世のsupervisorの記事がバージョンが3前提になっているものも多く、
もしsupervisor周りで思った動作をしないようであれば、「2系だから」というのを疑ってみたほうがいいかもしれません。
Deep Neural Networkの高速化について

Deep Learningは大量のデータを学習する学習フェーズと、学習した結果を用いて推論するフェーズに分かれます。

その中でも学習にかかるコスト、要するに学習時間が非常にかかりハイグレードのGPUを用いても数日から数週間かかることもまれではありません。Deep Neural Networkは文字通りNeural Networkが多層連なったもので、そのうちのConvolution Layerが計算量の多い行列の乗算を行うため色々と工夫されています。

行列A（M x K)と行列B（K x N)の乗算ではM x N x K回の積和演算を行います。高速な物体認識を行うYOLOでも以下のような回数の積和演算（MAC: Multiply Accumlate）が必要となるのです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/34750/5007d1a5-3c83-ebff-3780-4843d35fc30e.png)

## WinogradとFFT
Convolution LayerでWinogrdやFFTで別の空間に投影（変換）すると乗算の回数を減らすことができ、特にFFTでは行列の要素数が大きくなるほど絶大な効果を発揮します。NVIDIAが提供しているcuDNNでもこのWinogrdやFFTを用いるモードが実装されています。

ただ、この別空間に投影した場合、元に戻す処理が発生します。もし、この投影と逆投影を最初と最後だけにできればその恩恵にあずかることができるのでは、と提案されたのが[Fourier Convolutional Neural Network（FCNN)](http://ecmlpkdd2017.ijs.si/papers/paperID11.pdf)です。

ただ、比較的Network構成が簡単なAlexNetやVGGであっても、ReLUやMax Poolongをはさんでおり、この部分を変換されたFourie空間で行う方法が必要になります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/34750/9d24e914-142d-b3e5-f71c-8e65e5203735.png)
この図でのピンク部分を取り出す操作はローパスフィルターで、この論文ではFourier Poolingと呼んでおり、演算自体が必要なくなります。

提案されたFCNNではAlexNetにおいてMax Poolongの代わりにFourie　Poolingを使用し、エポックあたりの処理時間が通常のConvolutionの2435秒から65秒（画像サイズ:512の場合）に高速化されています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/34750/5d06dd34-2398-bbc0-244a-1f7b48b813de.png)

にわかには信じがたい高速化なんですが、もう一つ同様の論文があります。

[Reducing Deep Network Complexity with Fourier Transform Methods](https://arxiv.org/pdf/1801.01451.pdf)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/34750/211190a2-7190-076c-42d4-8df3d8e198ed.png)

図の(a)はcuDNNに実装されているFFTモードを使用した場合のConvolution Layerの処理を示しており、入力側でFFT,出力側でIFFT(FFTの逆変換）になります。畳み込み演算自体は早くなりますが、入力および出力時の変換処理がオーバーヘッドとなります。
図の(b)が入力時にFFTを行い、周波数空間のまま次のレイヤーに処理を渡し、全てのDeep Neural Networkを周波数空間の演算としています。論文によれば推論側でもこの周波数空間のまま保存したNeural Networkを使用しているとのことです。

気になるMax PoolongをFourier Poolingに置き換えた場合の誤差も問題ないようです（というか減少してる）。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/34750/3dc04b34-ff02-d825-e4bd-d2bd43411536.png)

githubにもソースコードがリンクされているのですが、なぜか現在削除されており、追試するための情報が不足しています。

## Very Efficient Training of Convolutional Neural Networks using Fast Fourier Transform and Overlap-and-Add

実際にFFTとその拡張版OaAを使用した場合の性能があったので紹介。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/34750/698fc663-5198-57ae-e9c9-29ff659f9112.png)
この図の縦軸は通常の畳み込みを行った場合との高速化倍率、横軸はカーネルサイズで、カーネルサイズが大きくなるほどFFTとその拡張版のOaAによる畳込みの効果が大きくなっている。ただし、VGGなどのよく用いられているモデルでのカーネルサイズは3なので、そのサイズの場合はそれほどご利益はない。
実行ディレクトリの場所を固定しないスクリプトを書く

自分で書いたスクリプトを見なおしてみると、そのスクリプトが置かれたディレクトリでしか動作しないスクリプトになっていることが多いものです。
ここでは、そのことを回避するちょっとした書き方の工夫を示します。


## **使用するデータの場所が見つからなくなることを防止する。**

相対pathで記述してあるファイルの場所は、スクリプトの実行のさせ方によって動作できなくなります。
その様子を次のスクリプトで示します。


```:data_path.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os

baseDir = os.path.dirname(__file__)
data = "data.txt"

if __name__ == "__main__":
    fullpath = os.path.join(baseDir, data)
    txt2 = open(fullpath, "rt").read()
    print(txt2)

    txt = open(data, "rt").read()
    print(txt)
```


そのスクリプトの実行結果は次のとおりです。


```
# スクリプトがあるディレクトリでは、どちらの書き方でも動作します。
$ python data_path.py 
Hello Python

Hello Python
$ cd ..

# スクリプトのないディレクトリでは、相対パスでの記述はエラーを生じます。
$ python data_path/data_path.py 
Hello Python

Traceback (most recent call last):
  File "data_path/data_path.py", line 13, in <module>
    txt = open(data, "rt").read()
IOError: [Errno 2] No such file or directory: 'data.txt'
$ 
```

このような工夫で、スクリプトが安定して動かせる範囲が広がります。


追記；別手法
実行ディレクトリを固定し、入出力のディレクトリを絶対pathで指定する。

```
$ python someScript.py  image.jpg
```

とスクリプトのディレクトリで、内部でこのディレクトリに対する相対pathでアクセスするデータファイルを用いているとしよう。


そのような場合には、他のディレクトリでこのようにしても動作しない。

```
$ python ~/script/someScript.py  image2.jpg
```


これを次のように実装して回避することができる。
まず、実行時のディレクトリで、入出力を絶対pathに変換しておく。
そのうえで、スクリプト内部で
os.chdir(スクリプトの置かれているディレクトリ)
したうえで、本来の処理を入出力に絶対pathを指定して実行しよう。
そうすれば、この場合も、実行ディレクトリを気にせずに済むスクリプトになる。



- [[Python] スクリプト実行ディレクトリを絶対パスで取得する](https://qiita.com/nagamee/items/b7d1b02074293fdfdfff)


---

Python3では
pathlib が便利です。
firebase storage のダウンロード URL 取得方法が SDK v5 で変わった

firebase storage にアップロードしたファイルのダウンロード URL 取得方法が JavaScript SDK v5 から変更になった。具体的には `FullMetadata.downloadURLs` が削除された。

### v4 までの取得の仕方

今まではアップロードした snapshot から直接ダウンロード URL を取得できた。

```js
const storageRef = firebase.storage().ref('hoge.txt')
storageRef.putString('example').then(snapshot => {
  snapshot.downloadURL
})
```

### v5 からの取得の仕方

改めて参照をしないといけない。

```js
const storageRef = firebase.storage().ref('hoge.txt')
storageRef.putString('example').then(snapshot => { 
  firebase.storage().ref('hoge.txt').getDownloadURL().then(url => {
    url
  })
})
```

- 参照: https://firebase.google.com/support/release-notes/js
Kubernetes認定試験CKADについて

# Kubernetes認定試験CKADについて

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96277/10dc0ae2-f446-7040-4bee-b234b9ec006b.png)

## CKADとは

- 詳しくは[正式サイト](https://www.cncf.io/certification/expert/cka/ckad/)にて説明がありますが・・・雑にまとめますと
- Certified Kubernetes Application Developerの略です
- 認証を取得しますと、**Kubernetes本家のCloud Native Computing Foundation(略: CNCF)とその代理者であるLinux Foundationからの承認**を得られます
- 実際に認定されているかどうかを確認するにはIDと名前を[ツール](https://training.linuxfoundation.org/certification/verify-certifications)を入れると証明を得ることができます
- 認証は~~24カ月の有効期限があります~~ 36ヶ月に延長されました(2019/11)
- 意味は"The Certified Kubernetes Application Developer exam certifies that users can design, build, configure, and expose cloud native applications for Kubernetes."とありますが
- 私自身は、これは要は**Kubernetesを利用するエンジニアとして一定のレベルにあることを確認できる証左**であると考えています
- Kubecon 2018(5/2)で大々的に[オープン](https://www.linuxfoundation.jp/cloud-containers-virtualization/cloud-native-computing-foundation-announces-launch-of-certified-kubernetes-application-developer-ckad-exam/)になりました

そのほかの内容につきましては[Agreement](https://www.cncf.io/certification/agreement/)に記載がございます。

## CKADのテストについて

- [試験概要](https://www.cncf.io/certification/expert/cka/ckad/)にあります通り、
    - 13% – Core Concepts (基本コンセプト)
    - 18% Configuration (設定)
    - 10% Multi-Container Pods (複数コンテナからなるpod)
    - 18% – Observability (ログなどによる確認)
    - 20% – Pod Design (Podの設計)
    - 13% – Services & Networking (Serviceとネットワーク)
    - 8% – State Persistence (状態の永続性)
- 詳細は[カリキュラム](https://github.com/cncf/curriculum/blob/master/certified_kubernetes_application_developer_exam_v1.0.pdf)にあります
- 試験時間は**2時間**です
- **USD$300** かかります

## テストの流れ

基本的には[正式サイト](https://www.cncf.io/certification/expert/cka/ckad/)の「ハンドブック」にあるのですが、

- 申し込みます
- 日程を選びます
- 1日前までなら再度日程調整が可能
- テストを受けます
- 失敗してもタダでもう一度受けられます

## テスト中の注意事項
- [CyberAgentの青山様に](https://developers.cyberagent.co.jp/blog/archives/15383/)にある通り、**試験にはブラウザが用いられ、それ以外に開くことが許されるのは１つのタブ、しかもアクセス先はkubernetes.ioに限られます**
- kubernetes.ioにあるリンクをクリックして、それがkubernetes.io以外にアクセスするものであったとしても、それを閲覧してはいけない、と制約に記載されています（変なリンクはクリックできないってことです）

## 補足：受けた結果

なんとか受かりました。正しいか不明ですがIDから察すると92番目か。。日本では10番以内に入ってるといいな。。前述青山様や[@CS_Tokuさんん](https://twitter.com/CS_Toku?lang=en)は受かってるみたい。

![certificate.jpg](https://qiita-image-store.s3.amazonaws.com/0/96277/b0ad18c8-a585-b744-8fe0-022fddd65671.jpeg)
SOFT SKILLS まとめ 【第1章(あなたが読んだどの本とも「この本」が違う理由)】

「SOFT SKILLS　ソフトウェア開発者の人生マニュアル」を、理解を深めるために1章をまとめてみました。

# はじめに
## 第1章　あなたが読んだどの本とも「この本」が違う理由
「ソフトウェア開発者の人生マニュアル」とは何か。ここで簡単に答えておく。それは以下である。

* ソフトウェア開発業界で評価を築くための手順を教えると同時に、肉体的、知的、精神的により協力で健康になるための方法を教えてくれる本

さらに詳しくいうと、この本にはたとえば以下のようなことが書かれている。

* 面接をハックし、最高の履歴書を書くための方法
* 大成功を収めるブログの作成方法
* 個人ブランドの構築方法
* 学習能力や生産性を大幅に引き上げる都ともに燃え尽き症候群に対処する方法
* 不動産投資のやり方
* 体重の減らし方
* 特別な人と出会う方法

つまり、「よりよい開発者になる方法をまとめて教えてくれる本」を作り、伝えたかったから、ソフトウェア開発者としての貴方の旅路を少しはらくにできるように、あなたがよりよいバージョンになることを支援できるように、そして何より、あなたが人生で孤独だと感じなくて済むようにするためだ。



私がこの本で言う「良いソフトウェア開発者」は、

* コードの書き方
* 問題の解き方
* ユニットテストの作り方がうまい人

ではなく、

* キャリアのマネジメント
* 目標の達成
* 人生の楽しみ方

という意味で言っている。

また、この本は、「あなたに何ができるか」についての本ではなく、「あなた」についての本だ。それはたとえば以下である

* あなたのキャリア
* あなたの生活
* あなたの心と身体
* あなたの魂

私はソフトウェアに対する「総合的な」アプローチを支持する。
つまり、よりよいソフトウェア開発者になりたいなら、人生におけるひとつ、二つの領域でなく、「ひとりの人間として全体をとらえる」必要があると考えている。

本書は、ソフトウェア開発者の生活の様々な側面に一つずつ注目していくために、全7部で構成されている。具体的には、以下である

* キャリア
* マーケティング
* 学習
* 生産性
* お金
* 健康
* 精神

最重要領域であるキャリアから始めるが、これは、たとえば人によりだが以下のような目標があると思う。

* 会社の出世街道を行く
* 独立してコンサルタントとして生計を立てる
* アントプレナー（起業家）になって自分の製品を作る

本書で、私の経験から、失敗談含め、貴方の求めているキャリアの違いによらない重要なスキルキャリアを積極的にマネジメントするにはどうすれば良いかについて知恵を伝授する。

マーケティングでは、以下のようなスキルについて実践的なアドバイスを送る

* パーソナルブランドを築く方法
* ブログを成功させる方法
* 講演・指導・執筆などを通じて名前を売る方法
* あなたがおそらく今まで考えたことないだろうと思われるその他の方法

キャリアの問題が片付いたら、次は知識の領域として、「学ぶことを学ぼう」へと進む。
ここでは、私が自分のキャリアを通じて開発した「10ステップの独学プロセス」を紹介する。また、

* メンターの見つけ方
* メンターになること
* 成功するために伝統的な教育や学位が必要か

といったテーマにも触れる。

次に「生産性を高めよう」では、編み出した「トップスピードでハイウェイに突っ込んでいけるシステム」や、また、

* 燃え尽き症候群
* テレビの見過ぎ
* モチベーションの見つけ方
* 昔ながらのハードワークのやり方

といった難しいテーマにも取り組む

次に「お金に強くなろう」では、将来のための資産運用を立案するために知っておかなければならない基礎を身につけてもらおう。具体的には以下のようなことである

* 収入を管理する方法の基礎
* 株式市場の本当の仕組み
* 不動産投資のやり方
* 借金の防ぎ方

続いて、第6部では、「やっぱり、体が大事」という、脂肪を落として筋肉をつけ、丈夫になるために必要な事柄をすべて取り上げる。具体的には以下である。

* 食事と栄養の基礎
* 食事が身体に及ぼす影響
* 体重を落とし、筋肉をつけ、あるいはその両方をてにいれるためのフィットネスプラン

最後に「負けない心を鍛えよう」では、精神状態や態度に影響を与えるテーマについて触れる。また、以下のような内容にも触れる

* 技術者が苦手とする愛や人間関係
* 成功をつかむためのブックリスト
Google Home に自動翻訳機になってもらった。


#要旨
・Google Home に話しかけて、翻訳した文章を発話してもらう
・翻訳はGoogle Translation API に頑張ってもらう
・限定的な状況で実用性があるかもしれない

##まずはフロー図
こんな感じで流します。
![フロー.png](https://qiita-image-store.s3.amazonaws.com/0/159653/e20b90ec-8876-53c6-7eae-7cdbedcabfe9.png)
①IFTTT→②Firebase→③Mac上に立てたnode.js→④Google Translation API→⑤node.jsでGoogle Homeを認識して出力
①~③は概ねこちらを参照しています。
[Google Home、IFTTT、Firebase、Node.js、irMagicianを使ってシーリングライトを音声操作する](https://qiita.com/miso_develop/items/c2008235cb4a8567cfa9)
④はGoogle Translation APIのドキュメントを参照しています。
[Translate API ドキュメント](https://cloud.google.com/translate/docs?hl=ja)
⑤はこちらを参照しています
[Google Home開発入門 / google-home-notifier解説](https://qiita.com/SatoTakumi/items/c9de7ff27e5b70508066)
順を追って説明します。


##①IFTTT
IFTTTではGoogleHomeで音声認識した文章を受け取ってFirebase にJSONをPUTしています。
図のようにIFTTTでは"$"で自由形式の文章を受け取ることができます。
![Ifttt1.png](https://qiita-image-store.s3.amazonaws.com/0/159653/6952417c-caf1-af47-a05b-6c10646c9bd6.png)
出力する際はbodyに"{{TextField}}"と入力すれば受け取った文章をそのままPUTできます。
URLにはFirebaseのURLを指定します。
![Ifttt2.png](https://qiita-image-store.s3.amazonaws.com/0/159653/fdaa6b43-bc6e-12f2-c2cb-55b16803fe00.png)


##②Firebase
IFTTTで指定したURLにJSONがPUTされます。
"googlehome/name.json"と階層を切っているので以下の図のように発話した内容がPUTされます。
![firebase.png](https://qiita-image-store.s3.amazonaws.com/0/159653/98f614d2-fcbb-08cc-0b11-6cb58df2b7f2.png)

##③Mac上に立てたnode.js
最初に記載した参照先と同様に、Firebaseの更新を監視するために以下のようなコマンドを打ってFirebase モジュールをインストールします。(node.jsのコードについては、他と一緒にまとめたもののURLを後で貼ります)

```
$ mkdir firebase
$ cd firebase
$ npm install firebase
```

##④Google Translation API
本件のキモである翻訳はGoogle先生に頑張ってもらいます。こちらにfirebaseの更新をトリガーとして日本語の文章を投げて、英語で返ってきた文章を受け取ります。

##⑤google-home-notifier
こちらを使用してnodeからGoogle Homeを探知して翻訳された文章を送信します。インストールは以下の通りです。

```
$ npm install google-home-notifier
```

##実行結果とコード
そして実際に動作した結果が以下の動画になります。
「ゴールデンウィークは楽しかったよ」と呼びかけたて「Golden week was fun.」と返答があることが確認できます。

[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/vsrRNouvOgE/0.jpg)](http://www.youtube.com/watch?v=vsrRNouvOgE)

以上、IFTTT・Firebaseの設定とnode.jsのコードを書くことでGoogle Home に自動翻訳機になってもらうことができました。コードは以下です。（筆者は本件で初めてnode.js を触りましたので、至らないところがありましたらご指摘いただければありがたい限りです。）
[Google-Home-Translator](https://github.com/whisky-shusuky/Google-Home-Translator/blob/master/bin/app.js)

##感想や用途など
今回翻訳機を作成しましたが、同じように翻訳するスマホアプリは探せば幾らでも有ります。持ち運び易さについてはGoogle Home よりもスマホのほうが高いことは明らかでしょう。
ただし、Google Home はスマホと異なり常時起動しているという特徴があります。（使いたい時だけ押し入れの奥からGoogle Homeを引っ張り出してきてコンセントにつなげる人はあまりいないでしょう）そのため、スマホのアプリを立ち上げてタッチするといった作業を行わずに声だけで起動することができます。スマホアプリに勝る点があるとしたらそこにあると考えています。
例えばPCで英語論文を執筆しているときに、適切な英単語が思い浮かばなかったときに、声を出すだけで翻訳できればPCから目を離すことなく作業が続けられるので便利ではないでしょうか。
また、応接間に置いておいて外国からの賓客に大して「我が社の通訳です！」と述べて使用して見せたりすればウケたりするかもしれません。（これはスマホとどちらがウケるか分かりませんが）
inputタグ、buttonタグでの画面遷移の記述について

#aタグ以外での画面遷移について
HTMLでの任意のページに画面遷移する際に一番最初に思いつくのはaタグですが、aタグでは表示がボタンではないため、見た目的にすこしアレな気がします。
そこで、aタグ以外のものをつかって任意のページに画面遷移できる方法がないか調べたところ、いくつかあったので記載していきます。

```HTML
  <!-- index.htmlへ遷移 -->
  <!-- 任意のページに画面遷移するやり方で一番オーソドックスな方法 -->
  <a href="./index.html">遷移</a>
```
###inputタグでの画面遷移について
最初にinputタグについてです。
inputタグでは、要素に次のものを追加することで、任意のページへ画面遷移できます。

```HTML
  <!-- 下記要素をinputタグの中に追記 ※○○○○○は任意のページ名 -->
  onclick="location:href='○○○○○.html'"

  <!-- index.htmlへ遷移 -->
  <!-- inputタグ全体としては下記のような感じ -->
  <input type="button" onclick="location.href='./index.html'" value="遷移">
```

直前のページに戻る場合はonclickの要素を下記のようにすればいいです。

```HTML
  <!-- 直前のページに戻る -->
  <input type="button" onclick="history.back()" value="戻る">
```

###buttonタグでの画面遷移について
次にbuttonタグについてです。
とはいっても、やり方はinputの時と同じでbuttonタグの要素にonclickを追加します。
onclick要素の中身も同じです。

```HTML
  <!-- index.htmlへ遷移 -->
  <button onclick="location.href='./index.html'">遷移</button>
```

直前のページに戻る場合もinputと同様です。

```HTML
  <!-- 直前のページに戻る -->
  <button onclick="history.back()">戻る</button>
```

この記事は、次のサイトを参考にさせていただきました。
ここに書いた記事以外にもいろいろとおもしろいことが書かれているので、見てみてください。
[参考サイト様](http://yume.hacca.jp/koiki/form/button-link.htm)
Prism入門 その3 - DelegateCommand

## はじめに

Prism入門3回目です。今回はDelegateCommandについて説明します。

MVVMではViewは描画しかできません。メソッドはVMに記述される必要があります。
Viewにメソッドは基本的に書かないので、バインドでメソッドを実行できるようにする必要があります。

そこで登場するのが、ICommandインターフェースです。
例えば、ICommandインターフェースを持ったクラスのプロパティをボタンでバインドするとボタンを押した時にコマンドが実行されます。

ただ、メソッドごとにICommandインターフェースを持ったクラスを用意するのはとても面倒です。そこでDelegateCommandを使用します。

## DelegateCommand

DelegateCommandは初期化時にラムダ式やメソッドを引数に指定できます。

引数には実行する内容と、実行できるか、の2つのメソッドを指定できます。

### cs

```
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;

namespace prismSample.ViewModels
{
    public class MainPageViewModel : BindableBase, INavigationAware
    {
        private string _title;
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }

        public DelegateCommand Clicked { get; private set; }

        public MainPageViewModel(INavigationService navigationService)
        {
            Clicked = new DelegateCommand(
                () => { Title = "Clicked;"; }, // 実行される内容
                () => true); // 実行できるか？
        }
    }
}
```

### Xaml

```
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" prism:ViewModelLocator.AutowireViewModel="True" x:Class="prismSample.Views.MainPage" Title="MainPage">
    <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
        <Label Text="{Binding Title}" />
        <Button Command="{Binding Clicked}" Text="Click!" />
    </StackLayout>
</ContentPage>
```

当然、アプリケーションの実行中にボタンを押せるかどうかが変化することがあります。
例えば、全ての入力欄に内容が入力された時や、他の処理が終わった後などです。

その時に実行できるか、のフラグが変化したかを通知する必要があります。
```ICommand.CanExecuteChanged``` を実行すればいいですが、
色んなところに実装が書かれることになり、とても面倒です。

そこで、DelegateCommandには、フラグが変更したかを楽に通知する方法があります。

それはObservesPropertyです。
今回はアプリケーションが処理を行なっている時はボタンを押せないような実装を想定しました。IsBusyプロパティとObservesPropertyが追加されています。

DelegateCommandの生成に続けてObservesPropertyを実行すると引数に指定されているプロパティが変更するたびに実行できるかの可否の通知が行われるようになります。

```
private bool _isBusy = false;
public bool IsBusy
{
    get { return _isBusy; }
    set { SetProperty(ref _isBusy, value); }
}

public DelegateCommand Clicked { get; private set; }

public MainPageViewModel(INavigationService navigationService)
{
    Clicked = new DelegateCommand(
        () => { Title = "Clicked;"; },
        () => !IsBusy).ObservesProperty(() => IsBusy);
}
```

こうすると全てのコマンドに関する実装が一箇所にまとまり、挙動がわかりやすくなります。

## 最後に

他にもPrismにはコマンドを補助する機能があります。
興味がありましたら、調べてみてください。
GoでもRailsのようにtry(:hoge)で安全にプロパティを取得する

最近Goを始めた新人Gopherです。
今まではRailsでWebアプリケーションを作ってました。

基本的にStructを定義していく感じか〜〜🤔ﾌﾑﾌﾑ
と読み方がだいたいわかってきたある日。

# とあるサードパーティのサービスが返すJSONのプロパティ読みづらい！！

そのサードパーティ製ライブラリには以下のような定義がされてました。

```golang:Hoge.go
type Hoge struct {
  Fuga map[string]interface{}
}
```
で、実際のオブジェクトはこんな感じ

```
{
  "foo": {
    "bar": {
      "first": 1,
      "second": 2
    }
  }
}

```
Interface{}・・・😭

## できることならこんな感じで書きたい💡

```
first := hoge["foo"]["bar"]["first"] //=> 1
```

でもGoだとこう書かないといけない。

```golang:hoge.go
first := hoge["foo"].(map[string]interface{})["bar"].(map[string]interface{})["first"]
```


このままだとAPIの仕様変更でフォーマットが変わるたびにpanicしかねないので、

- nilチェック
- 型アサーション

はした方が良さそうですよね。（恐らく以下のような実装になると思います）

```golang:hoge.go
first := func() interface{} {
  if foo := hoge["foo"]; foo != nil {
    if _foo, ok := foo.(map[string]interface{}); ok {
      if bar := _foo["bar"]; bar != nil {
        if _bar, ok := bar.(map[string]interface{}); ok {
          return _bar["first"]
        }
      }
    }
  }
  return nil
}()
fmt.Println(first)
```

😭

## Railsを思い出した

RailsだとActiveSupportの機能で

```ruby:hoge.rb
first = hoge.try(:[], :foo).try(:[], :bar).try(:[], :first)
```
で取得でき、存在しないキーを参照していた場合nilが返却されます。安心安全☺️


ということで、
## Goでも似たようなことができるライブラリを作りました！！

#### Go TryableMap
https://github.com/HaraKeisuke/go-tryablemap


### 使い方

```golang
_tryable := tryablemap.NewTryableMap(hoge)

first := _tryable.Try("foo").Try("bar").Value("first") 

fmt.Println(first) //=> 1
```
基本的には
- map[string]interface{}はとりあえずNewTryableMapに渡す
- 掘りたいパラメータはTryで掘り進め、取得したいパラメータはValueで取得する
- TryもValueも値が存在しなければnilを返す。

で掘る事ができます💡

- NewTryableArrayでArrayも安全に掘る事ができたり、

```golang:array.go
array := [][]int{
  []int{1, 2, 3}, []int{4, 5, 6}, []int{7, 8, 9},
}

tryable := tryablemap.NewTryableArray(array)

result := tryable.TryArray(0).Value(0) //=> 1
```

以下のようなArrayとMapが混ざっている場合は、

```json:arraymap.json
{
  "hoge": [
    { "first": [1, 2, 3] },
    { "second": [4, 5, 6] }
  ]
}
```

- TryとTryArrayを使い分ける事で掘る事ができます😆
 
```go:arraymap.go
tryable := tryablemap.NewTryableMap(arraymap)

first := tryable.TryArray("hoge").Try(0).TryArray("first").Value(0)

fmt.Println(first) //=> 1
```

**Tryは返却値がMapのとき**に使い、**TryArrayはArrayのとき**に使うイメージです。
Value関数の引数keyは**Mapの時はString**、**Arrayの時はInt**を渡すことになります。



安全な芋掘りでより良いGoライフを！


.
docker上でrocketchat+nginxを構築

# docker上でrocketchat+nginxを構築
## rocketchatの構築
docker-composeでrocketchat+mongodb+hubotを構築します。

```rb:docker-compose.yml
  rocketchat:
    image: rocketchat/rocket.chat:latest
    restart: unless-stopped
    volumes:
      - ./rocketchat/uploads:/app/uploads
    environment:
      - PORT=3000
      - ROOT_URL=http://localhost:3000/rocketchat
      - MONGO_URL=mongodb://mongo:27017/rocketchat
      - MONGO_OPLOG_URL=mongodb://mongo:27017/local
      - MAIL_URL=smtp://smtp.email
#       - HTTP_PROXY=http://proxy.domain.com
#       - HTTPS_PROXY=http://proxy.domain.com
    depends_on:
      - mongo
    ports:
      - 3000:3000
    labels:
      - "traefik.backend=rocketchat"
      - "traefik.frontend.rule=Host: your.domain.tld"

 mongo:
    image: mongo:3.2
    restart: unless-stopped
    volumes:
     - ./data/db:/data/db
     #- ./data/dump:/dump
    command: mongod --smallfiles --oplogSize 128 --replSet rs0 --storageEngine=mmapv1
    labels:
      - "traefik.enable=false"

  # this container's job is just run the command to initialize the replica set.
  # it will run the command and remove himself (it will not stay running)
  mongo-init-replica:
    image: mongo:3.2
    command: 'mongo mongo/rocketchat --eval "rs.initiate({ _id: ''rs0'', members: [ { _id: 0, host: ''localhost:27017'' } ]})"'
    depends_on:
      - mongo

  # hubot, the popular chatbot (add the bot user first and change the password before starting this image)
  hubot:
    image: rocketchat/hubot-rocketchat:latest
    restart: unless-stopped
    environment:
      - ROCKETCHAT_URL=rocketchat:3000
      - ROCKETCHAT_ROOM=GENERAL
      - ROCKETCHAT_USER=bot
      - ROCKETCHAT_PASSWORD=botpassword
      - BOT_NAME=bot
  # you can add more scripts as you'd like here, they need to be installable by npm
      - EXTERNAL_SCRIPTS=hubot-help,hubot-seen,hubot-links,hubot-diagnostics
    depends_on:
      - rocketchat
    labels:
      - "traefik.enable=false"
    volumes:
      - ./scripts:/home/hubot/scripts
  # this is used to expose the hubot port for notifications on the host on port 3001, e.g. for hubot-jenkins-notifier
    ports:
      - 3001:8080
```

インストール＆起動します。

```
$ docker-compose up
```

起動したらcurlで確認します。それっぽいhtmlが表示されたらok。

```
$ curl http://localhost:3000/rocketchat
```

## nginxにリバースプロキシを設定
nginxの構築は[こちら](/ryohryp/items/bc9200e7fdcbceff57d0)の記事を参照。

rocketchatのへのリバースプロキシの設定を追加します。

```rb:mysite.template
server {
## 省略 ##
    location /rocketchat/ {
        proxy_pass http://rocketchat:3000/rocketchat/;
        proxy_redirect off;
    }
}
```

プロキシしているurlのホストがrocketchatとなっていますが、docker上のサービス名と一致しています。こうやって指定することで、アドレス解決はdockerが勝手にやってくれます。

nginxを再起動して設定を反映します。

```
$ docker-compose restart web
```

ブラウザから`http://IPアドレス/rocketchat/`にアクセスするとrocketchatの初期設定画面が表示されます。

## 参考
docker-compose.yml example
https://raw.githubusercontent.com/RocketChat/Rocket.Chat/develop/docker-compose.yml

Configuring SSL Reverse Proxy
https://rocket.chat/docs/installation/manual-installation/configuring-ssl-reverse-proxy/
続々ThermoMSで取得したデータを処理する

#アベレージMSを取得する
前回ほどの難易度はないけれど、とにかく引数が多い。
ObjectPeakFlagsなど、将来的に利用するもの（現在は使ってない）も多いので、将来的に構造体で渡していきたい。
前回のTICと応用するとImaging MSデータの作成が可能になると思う。

```

        private async void textBox1_DragDrop(object sender, DragEventArgs e)
        {
            string[] fileName = (string[])e.Data.GetData(DataFormats.FileDrop, false);
            Series mz = await imgmz(fileName);
            chart.Series.Add(mz);
        }

       async  private Task<Series> imgmz(string[] fileName)
        {
            chart1.Series.Clear();
            MSFileReader_XRawfile rawfile = new MSFileReader_XRawfile();

            rawfile.Open(fileName[0]);
            rawfile.SetCurrentController(0, 1);

            int totalscannum = 0;
            rawfile.GetNumSpectra(ref totalscannum);
            int FstScanAv = 1;
            int LastScanAv = totalscannum;
            int FstStBGAv = 0;
            int FstLastStGBAv = 0;
            int SecStBGAv = 0;
            int SecLastStGBAv = 0;
            string Bstrfilter = null;
            int IntensityCutOff = 0;
            int IntensityCutOffValue = 0;
            int MaxNumberOfpeaks = 0;
            int Centroidresult = 0;
            double CentroidPeakWidth = 0.0;
            object ObjectMSList = null;
            object ObjectPeakFlags = null;
            int IntArraysize = 0;

            Series mz = new Series();

            await Task.Run(() =>
            {
                rawfile.GetAverageMassList(ref FstScanAv, ref LastScanAv, ref FstStBGAv, ref FstLastStGBAv, ref SecStBGAv, ref SecLastStGBAv,
                Bstrfilter, IntensityCutOff, IntensityCutOffValue, MaxNumberOfpeaks, Centroidresult, ref CentroidPeakWidth, ref ObjectMSList, ObjectPeakFlags, ref IntArraysize);
                double[,] avmass = (double[,])ObjectMSList;
                for (long k = 0; k < avmass.GetLength(1); k++)
                {
                    mz.Points.AddXY(avmass[0, k], avmass[1, k]);
                }
            });

            rawfile.Close();
            return mz;
}
```
Pure-FTPd-MySQLでWrong SQL parametersが出たときすべきこと

Pure-ftpdを使っていて、困ったことがあった。

```shell
tail -n 100 /var/log/syslog

pure-ftpd: (?@foopc) [WARNING] Wrong SQL parameters : [SELECT Password FROM ftpd WHERE User="testuser" AND status="1" AND (ipaccess = "*" OR ipaccess LIKE "192.168.1.10")]
```

ところが、SQLパラメタは誤って(wrong)いない。
原因を考えること２日間、ついに理由がわかった。

Pure-ftpd用のMySQLユーザーにselectの 権 限 を 与 え て い な か っ た だ け

DB接続エラーに関しては、起動時にmysql.confが間違っていると指摘するという振る舞いをする。ところが、権限不足はWrong SQL parametersと言ってしまう。
パラメタを何回も確認したじゃないか。。。。。

でもね、vsftpdよりは優しいと思うの。彼は無口だから。

まぁとにかく、pureftpでmysqlのエラーが出たときはあらゆることを考えないといけない。



なお、サーバーの構築はhttps://symfoware.blog.fc2.com/blog-entry-1979.html をそのまましていることを留意されたい。
[Rails] trailing_slash: trueなルーティングのテストに失敗するとき

RailsのController Specで、`trailing_slash: true`なrouteをテストしようとしてハマった。

# 具体例

```ruby:config/routes.rb
get 'test/ok', to: 'test#ok'
get 'test/ng', to: 'test#ng', trailing_slash: true
```
```ruby:app/controllers/test_controller.rb
def ok
  render plain: test_ok_path # => /test/ok
end

def ng
  render plain: test_ng_path # => /test/ng/
end
```

ブラウザからそれぞれアクセスする（末尾スラッシュは付けても付けなくても構わない）と正しく動作する。
しかし、`test#ng`のテストを普通に書くとNo route matchesになってしまう。

```ruby:spec/controllers/test_controller_spec.rb
describe 'GET #ok' do
  subject { get :ok }
  it { is_expected.to be_successful }
end

describe 'GET #ng' do
  subject { get :ng }
  it { is_expected.to be_successful }
end
```
```
ActionController::UrlGenerationError:
  No route matches {:action=>"ng", :controller=>"test"}
```

# 解決方法

`get`の引数`params`に`trailing_slash: true`を含めればよい。

```ruby:spec/controllers/test_controller_spec.rb
describe 'GET #ng' do
  subject { get :ng, params: { trailing_slash: true } }
  it { is_expected.to be_successful }
end
```

`params`に入れるのってクエリパラメータだけじゃないんですね…。
公式ドキュメントの記述は見つけられていないので、ご存知の方がいれば教えてほしい。
続・ThermoMSで取得したデータを処理する

#.RawファイルからTIC,XICを取得する
MSfileReader3.2から仕様が変わったため備忘録
MSレンジの指定方法が特徴的でStartMS1, StartMS2には
"195.1-195.3"のような開始MS-終了MSをstringで入力する必要がある。
intChroOperatorは0:処理なし、1:StartMS1+StartMS2, 2:StartMS1-StartMS2の処理を行う。
測定時間はdouble型の測定開始からの秒数が得られるので、通常のキャストだとエラーが発生する。

```

        private async void textBox1_DragDrop(object sender, DragEventArgs e)

        {

            string[] fileName =(string[])e.Data.GetData(DataFormats.FileDrop, false);
            Series tic = await ticdraw(fileName[]);
            chart.Series.Add(tic);

        }
async private Task<Series> ticdraw(string[] fileName)
        {

            chart.Series.Clear();
            MSFileReader_XRawfile rawfile = new MSFileReader_XRawfile();

            rawfile.Open(fileName[0]);
            rawfile.SetCurrentController(0, 1);

            double lowmass = 0;
            double highmass = 0;
            rawfile.GetLowMass(ref lowmass);//ここを開始MSとして指定する
            rawfile.GetHighMass(ref highmass);//ここを終了MSとして指定する

            int intChrotype = 0;// (0=MSrange,1=TIC,2=BasePeak) 
            int intChroOperator = 0; //
            int intChroType2 = 0; //
            string stringBstrFilter = null;
            string stringMSrange1 = null;
            stringMSrange1 = lowmass.ToString() + "-" + highmass.ToString();
            string stringMSrange2 = null;/
                                   
            double DoubleDelay = 0.0;
            Series tic = new Series();

            double DoubleStartTime = 0.0;
            double DoubleEndTime = 0.0;
            int intSommthingType = 0;
            int intSmoothingValue = 0;
            object varChrodata = null;
            object varPeakFlags = null;
            int intArraySize2 = 0;

            await Task.Run(() =>
            {

                rawfile.GetChroData(intChrotype, intChroOperator, intChroType2, stringBstrFilter, stringMSrange1, stringMSrange2, DoubleDelay, ref DoubleStartTime, ref DoubleEndTime, intSommthingType, intSmoothingValue, ref varChrodata, ref varPeakFlags, ref intArraySize2);
            double[,] Chrodata = (double[,])varChrodata;

                for (long l = 1; l < intArraySize2; l++)
                {
                    TimeSpan time = TimeSpan.FromMinutes(Chrodata[0, l]);//測定時間を取得する場合
                    tic.Points.AddXY(l, Chrodata[1, l]);
                }
            });
            rawfile.Close();
            return tic;
        }
```
MySQLでJDBCを使う際にSSLを有効にするかどうか。

SSLってネットワーク系のライブラリでしばしば惑わす存在。
存在を意識している女の子に対する感情と同じようなものです。

```java
 Class.forName("com.mysql.jdbc.Driver");
```

SSLをオンにしたい

```java
jdbc:mysql://localhost:3306/database?verifyServerCertificate=false&useSSL=true
```
SSLを有効にしたうえ、証明書をチェックしない。


オフにしたい

```java
　jdbc:mysql://localhost:3306/database?useSSL=false
```


#引用文献
http://www.sumimasen.com/tech/34189756.html
WEB広告運用について

某テクノロジースクールのweb広告運用講座に参加してきたので、備忘録までに内容をまとめます。
箇条書きなのは悪しからず。

**デジタルマーケティングにより、**
お客との接点が増えた。
　①即効性のある効果が期待できる
　②ターゲットを詳細に選ぶことができる
　③データを可視化できる（効果があったかどうか

**広告の役割とは？**
・お客が購入に到るまでのプロセス「知っている⇨興味がある⇨比較⇨購入しようと決意する」の全てを担う
また、一つだけでなく、客層によって複数使い分けることが最適

**良質なWEB広告を決める三大要素**
◎広告のターゲット
◯広告のクリエイティブ（デザイン
◯広告のコピー

**[最重要]広告のターゲットの決め方**
　・購買欲求（どのくらい必要としてるか）
　・顧客属性
　の２要素で判断。

また、顧客層は「低関心層」「潜在層」「顕在層」の３つに分けられる。

事業を始めたばかりであれば、欲しいけど買うか悩んでいる人（顕在層）へ向けて宣伝する。
一方、軌道に乗ったならば、興味がある、欲しい人（潜在層）へ
最後に、全国的に宣伝（低関心層向けのマス広告

⇨先ずは、いかに顕在層へアプローチするか？
ただし母数は少ないので、低関心層へアプローチを変える必要有り


**顧客属性について**

効果的な広告を打つために
１）適切な客へ配信する
２）広告のコピーやクリエイティブ訴求力を高める

　⇨①ターゲットとなる代表的な人物像をペルソナという。
　　　（サービスを利用する典型的ユーザー像をイメージしよう）
　　　
　ヒアリングを通してペルソナを作ることも大事。リリース前に感触を試し、
良い反応をしたユーザーをペルソナとする。


⇨②広告のクリエイティブについては、デザインのどれが当たるかはわからない。時間をかけない。気軽にやろう


**最小限のクリエイティブの選び方**
・文字は全体の２０％に収まるようにする（そういうルールのSNS多い
・４象限で区切ってテストする。（例：フォーマル⇆カジュアル、男性⇆女性）
・海外の無料画像サイトは高品質で良い

**良い広告コピーの条件**
・自分のためのものとわかる：ターゲットは必ず絞り込む
・顧客のニーズを的確に捉える：根本的悩みを解決したい悩みや、やらなければならない事に注目する
・将来像を想像することができる：どんな素晴らし未来が待っているかを想像させるコピー

**広告コピーのテクニック！**
①具体的な数字：91.3%のお客様が満足！　・・・納得しやすくなる
②緊急性：数量限定、期間限定、残りわずか
③簡易性：始めての方でも、たった7日間でも
④権威性を借りる：あの◎◎も利用している、医者も認める
⑤文章はなるべく短く、端的にする

**代表的なWEB広告**
１　リスティング広告：検索エンジンに連動して表示される広告
２　ソーシャルメディア：SNS上に表示する。低予算１万でいける！
３　アフィリエイト：比較サイトや体験記のようなブログは実はアフィリエイト広告である。赤字になりづらい。広告者を選ぶの大変

ディスプレイネットワーク広告：google、yahooの提携する複数のWEBサイトで自動で表示される広告（あまりターゲットできない
純広告：特定のサイト内の広告枠を買取、表示させる広告。高い　３００万、２００万
動画広告：youtubeなど。広告効果は高め。情報量多く、ブランディングになる。
リターゲティング広告：一度訪問した人へ表示できる。立ち上げてすぐでは効果が薄い

選ぶ基準は購買欲求、顧客属性で決めましょう

**購買欲求別WEB広告の選び方**
低関心層、（動画広告、純広告）
潜在層、（ソーシャルメディア広告、ディスプレイネットワーク広告）
顕在層（リスティング、リマーケティング、アフィリエイト広告）


**顧客属性**
ツイッターはフォロワーターゲティングができる。（例：AKBをフォローしてる人）
LINEのタイムラインはあまり使われていない。が、ユーザーは多い
FaceBookは社会人男性向け
インスタは女性向け
　
**迷った時に出せば良い広告は**
GOOGLE、FACEBOOK
⇨推す理由
　詳細なターゲティングが可能、機械学習による最適化が優れている

**まず広告を始めるならば**
①検索する顕在層の獲得；リスティング、アフィリエイト
②検索しない顕在層：FACEBOOK、リマーケティング


**WEB広告の予算の決め方**
商品ではなく、客１人当たり

**WEB広告の予算を決める③つの変数**
平均売上：
平均コスト：
目標とする利益：

広告費は最後に考える

LTV＝顧客一人あたりの売上（リピートも想定して計算
顧客一人あたりの平均コスト：広告費以外を全て計算する

目標とする利益にルールはない。会社として必要であると考える利益率から計算するのが多い



**広告は改善し続けることが大前提**
１、１回で最も効果の高い広告を作ることは不可能
２、同じ広告で成果を上げ続けるのは不可能

PDCAを常に回し続けることが大事
・目標を正しく設定する

KPI(KeyPerformanceIndicator)：目標とすべき最も重要な指標のこと
　　   CTR:見たうちのクリック率 
         CPA:顧客１名を獲得するためのコスト

　　　２つの指標を見て、広告の価値を図らないといけない

**広告テストの原則は、A/Bテスト**
　検証要素：ターゲット。コピー。クリエイティブ

改善のためには、必ず仮説を持つこと。PDCA
A/Bテストでは必ず一つの要素だけを変える。

数字を元に分析を行う（定量的に検証する）
仮説を元に次のアクションプランを決める


FACEBOOKのCTRは1％超えが優秀
2％はすごい広告

GOOGLEは5％以下は直した方が良い
Rubyで言語処理100本ノック 第1章:準備運動 (00 ~ 02)

# 趣旨
今までなんとなく触っていたRubyの勉強とチャレンジの備忘録として、
[言語処理100本ノック 2015](http://http://www.cl.ecei.tohoku.ac.jp/nlp100/) をやっていきたいと思います。


間違いがあればご指摘いただけると嬉しいです。

# 00.文字列の逆順
> 文字列"stressed"の文字を逆に（末尾から先頭に向かって）並べた文字列を得よ．

```ruby
puts "stressed".reverse
```

# 01.「パタトクカシーー」
> 「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ．

``` ruby
str = "パタトクカシーー"
puts str.chars.select.with_index{ |c, i| i.even? }.join
```

# 02.「パトカー」＋「タクシー」＝「パタトクカシーー」
> 「パトカー」＋「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ．

``` ruby
array = ["パトカー", "タクシー"]
array.map{ |str| str.chars }.transpose.join
```

Array\#transpose はネストされた配列を行列と見立てて、行と列を入れ替えてくれるんですね。素敵です。
WEBサービスを配信したい！その①


## きっかけ
- フルスタックエンジニアとして活躍したいから
- アプリ開発関連のブログを作りたいと思ったから
- 副業(アフィリエイトというもの)
 
## 作るもの

 ブログ・・・開発関連のブログ


## 計画（変更する可能性あり）
　言語　HTML CSS PHP
　GitHubを使用して、タスク管理する
　
　5月　HTML と CSS の学習
　⬇︎
　6月　ブログの企画、設計（終わり次第、開発開始）　PHPの学習
　⬇︎
　7月　開発中
　⬇︎
　8月　完成
　

## HTMLの基礎的な部分

```
<!DOCTYPE html>
<html lang="en">
 <head>
    <meta charset="UTF-8">
    <title>Title</title>
 </head>
 <body>
 <!--コードを記入することで、画面に反映される-->
 </body>
</html>

```

基本的な形
　文字コードはUTF-8を設定
　言語を英語に設定している（日本語ならlang="ja"にしておくべき）
　headのtitleはブラウザの上にあるタブのタイトルを設定できる
　bodyに基本的にコードを追加していく
　
bodyの内部に入れる
　headerには、ロゴやナビゲーションを入れる
　mainには、本文は入れる

```
　<header>
　<!--コードを記入することで、上部の画面に反映される-->
　</header>
　
　<main>
　<!--コードを記入することで、メインの画面に反映される-->
　</main>
　
　<footer>
　<!--コードを記入することで、下部の画面に反映される-->
　</footer>
```

ナビゲーション（タブや表示位置を遷移するときに使用）
 ulは番号なし
 olは番号あり
 liはリストのアイテム
 <a href="~" に新しい画面に飛ぶ　target="_blank" で新規タブに表示する

```
<nav>
    <ul>
      <li><a href="applePage.html" target="_blank">ホーム</a></li>
      <li>店舗案内</li>
      <li>問い合わせ</li>
      <li>採用情報</li>
      <li>その他</li>
    </ul>
  </nav>
```

## 次回の投稿日
　5月23日に投稿したいな
Pythonからdatasetを使ってMySQLに接続する


# 概要

Pythonプログラムからdatasetを使って、MySQLに接続する。


## datasetとは
https://github.com/pudo/dataset/


# 環境
 * Ubuntu 16.04
 * Python3.5
 * virtualenv環境を想定
 * MySQLはDockerコンテナとして稼働


# 手順
## datasetをインストール

```
sudo apt-get install libmysqlclient-dev
pip install mysqlclient
pip install dataset
```

## MySQL関連

```
# rootユーザでログイン
mysql -h 127.0.0.1  -uroot -pmysql

# IPアドレスは環境に合わせて
mysql> create user 'user1'@'172.17.0.1' IDENTIFIED BY 'password';
mysql> create database user1;
mysql> grant all on user1.* to 'user1'@'172.17.0.1';


# 作成したユーザでログイン
mysql -h 127.0.0.1  -uuser1 -ppassword

# テーブル作成
mysql> use user1;
mysql> create table foo (id int, name  varchar(10));
mysql> insert into foo values(1, "a");
Query OK, 1 row affected (0.03 sec)

mysql> insert into foo values(2, "b");
Query OK, 1 row affected (0.07 sec)

mysql> insert into foo values(3, "c");
Query OK, 1 row affected (0.02 sec)
```

## プログラム

```Python
import dataset

db = dataset.connect('mysql://user1:password@127.0.0.1/user1')
table = db['foo']

values = table.all()
for val in values:
    print(val['id'], val['name'])
```
【Go】ファイルアップロード

#技術書で勉強した内容をまとめたい
のでちょっとずつ投稿していこうと思います。
みなさんGoの学習は捗っているでしょうか？僕は全然です(爆)
あーいつになったらGoのエンジニアになれんだろー、って毎日思いながらjavaやってます笑。

というかたまにGoって初心者にもオススメの言語！みたいな記事見るんですけど、本当にそうなんですかね？
ほんとにまっさらな初心者でGoやってる人身近にいないのでわからないのですが、普通に簡単な言語じゃないので、自分はあまり初心者にはオススメできない。それは自分がちゃんと理解できてないからなんですが。

確かに動的型付け言語なのにシンプルに記述できて環境構築等の煩わしい作業が少ないという点では確かに学びやすさというのははわからなくもないのですが。
最初にC言語やjavaで挫折している経験があるからか、初心者にはもっと優しい言語でよいというのが自分の考え。

自分の勝手なイメージではC言語の経験者は余裕、javaの経験者もまぁ、勉強しながらなら大丈夫(ちなみにこの中に自分は入っていない)。それ以外の人、ポインタ？なにそれ美味しいの？から始まる。

#本題
読んでいる技術書はこちら
[Goプログラミング実践入門](https://www.amazon.co.jp/Go%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-%E6%A8%99%E6%BA%96%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%A7%E3%82%BC%E3%83%AD%E3%81%8B%E3%82%89Web%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E4%BD%9C%E3%82%8B-impress-gear%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-Sheong-Chang-ebook/dp/B06XKPNVWV)

#Goでのファイルアップロードについて
今回はGo言語でのファイルアップロードの実装を見ていきたいと思います。
実は他の言語でのファイルアップロードをやったことがないので、Goではじめてファイルアップロードをやることになります。再度言うが一応javaのエンジニア(を名乗っている)。
fileをクライアントから送信して、サーバー側で受け取ったら、中身をレスポンスに表示するという内容。

```html:index.html
<html>
--略--
 <body>
  <form action="http://localhost:8080/process?hello=qiita&article=123" method="post" enctype="multipart/form-data">
   <input type="text" name="hello" value="huji"/>
   <input type="text" name="post" value="456"/>
   <input type="file" name="uploaded">
   <input type="submit">
  </form>
 <body>
</html>
```
ファイルを受信するにはハンドラ関数を以下の通り設定
ちなみに1024は最大バイト数。

```golang:server.go
func process(w http.ResponseWriter, r *http.Request){
  r.PrseMultipartForm(1024)
  fileHeader := r.MultipartForm.File["uploaded"][0]
  file,err := fileHeader.Open()
  if err == nil {
    data, err := ioutil.ReadAll(file)
    if err == nil{
      fmt.Fprintln(w, string(data))
    }
  }
}

func main() {
  server := http.Server{
    Addr: "127.0.0.1:8080",
  }
  http.HandleFunc("/process", process)
  server.ListenAndServe()
}
```
実はこのprocess関数、下の書きかたで大丈夫。
FormFileメソッドでfileとfileHeaderを同時に取得できるからこれだけでok。
今回はfileしか使わないが、一発でfileを取得できるのはすばらしい。

```golang:server.go
func process(w http.ResponseWriter, r *http.Request){
  file,_,err := r.FormFile("uploaded")
  if err == nil {
    data, err := ioutil.ReadAll(file)
    if err == nil{
      fmt.Fprintln(w, string(data))
    }
  }
}

```

とまぁ、今回はこんな感じです。
なんかツッコミあったら是非是非お待ちしております。
Git: リモートのリポジトリを別のリモートリポジトリへコピーする



Bitbucketのリポジトリを、別のすでに作成されたリポジトリへのコピーをする方法です。

※これからリポジトリを新規作成する場合は「リポジトリのインポート」機能を使いましょう。


仮に以下のリポジトリを使うこととして進めます。

- 移行元: bitbucket.org:user_a/abc.git
- 移行先: bitbucket.org:user_z/xyz.git

### 手順概要
1. リモートリポジトリのbearをミラーオプションでクローン
2. originのPush先を移行先に切り替え
3. 移行元に変更があれば取り込み
4. 移行先にすべてをPush


### 手順

```bash
# 1: リモートリポジトリのbearをミラーオプションでクローン
git clone --mirror git@bitbucket.org:user_a/abc.git abc-mirror

# 2: originのPush先を移行先に切り替え
cd abc-mirror
git remote set-url --push origin git@bitbucket.org:user_z/xyz.git

## [確認]　Fetchが移行元でPushが移行先の状態になっている
git remote -v
#> origin	git@bitbucket.org:user_a/abc.git (fetch)
#> origin	git@bitbucket.org:user_z/xyz.git (push)

# 3: 移行元の変更を取り込み
git fetch -p origin

# 4: 移行先にすべてをミラーオプションでPush （強制上書きなので注意）
git fetch -p origin
git push --mirror
```

### リポジトリ利用者にリモートURLを変更してもらう

みんなにリモートURLを切り替えてもらえば完了！
`git remote set-url origin git@bitbucket.org:user_z/xyz.git`

もし移行元に変更があった場合、手順４を繰り返せば良いが、移行先に何かがPushされていた場合はすべてなかったことになるので注意。

参考：[Mirroring a repository in another location](https://help.github.com/articles/duplicating-a-repository/#mirroring-a-repository-in-another-location)