思いつきで作ったプログラムをSinatraに組み込みHerokuにデプロイする

## 先日作ったプログラム
Qiitaにバズる記事をあげたいが、俊逸なタイトルが想像力が欠如した筆者には思い浮かばない。
人類の叡智であるプログラムと俊逸さの申し子である小説家になろうを頼りに筆者の低脳ぶりを補ってもらおう。

そうして作成したプログラムが下記のリンクにある。

[どうやら魅力のタイトルづけは完全無敵のようですね](https://qiita.com/ObaTakeshi/items/3f64a1e43eed74483e3c)

このプログラムも結果から選ぶ手間はあるが、タイトルのおかげか、拙筆の中で最大級の評価(いいね)を誇った。
これに気を良くした筆者は、作成したプログラムを皆に使ってもらおうとHerokuにあげるのだった。

[なろうパロディ☆タイトルジェネレータ](https://aqueous-wildwood-63896.herokuapp.com/)

## 前作業
先日作成したプログラムは、小説になろうAPIの結果をファイルに保存し、形態素解析などの処理を行なっている。
しかし、HerokuにMecabを載せるのは、気が滅入る。
参考: [Herokuでpython+mecab+ffmpegを使う](https://qiita.com/Sashimimochi/items/2da79bda71dfc1b555cd)

処理自体の入力は埋め込むキーワードのみであり、形態素解析をローカルで行い結果を保存してデプロイするのが道理である。
幸いRubyには、オブジェクトをファイルに保存するMarshalモジュールが存在したので、ありがたく使わせていただいた。

```ruby
split_titles = []
noun_idxs = []
for t in titles do
    split_title, noun_idx = tagger_title(t)
    split_titles.push(split_title)
    noun_idxs.push(noun_idx)
end
Marshal.dump(split_title, open('split_titles.marshal', 'w'))
Marshal.dump(noun_idxs, open('noun_idxs.marshal', 'w'))
```
tagger_title()は形態素解析の結果と名詞の位置を返す関数である。(中身が気になる方は一番上の拙筆をご参照)
`Marshal.dump(object, file)`でobjectをfileに保存することができる。
また、`Marshal.load(file)`で保存したobjectをfileから読み込むことができる。

## Sinatraアプリの作成
今作の動作は、
1. ユーザにキーワードを入力させる
2. キーワードをもとに前回のプログラムを走らせる
3. 結果を出力する
とした。
SinatraはWebフレームワークであり、一発アイデアありき単純な今作にはうってつけである。

インストールには、下記のようにGemfileを記述し、`bundle`でインストールするとよい。

```:Gemfile
source "https://rubygems.org"

gem 'sinatra'
gem 'sinatra-contrib'
```
今回の欲しい処理をSinatraで実現しようと思うと下記のプログラムとなった。
まずは、リクエストを受けとる`app.rb`について紹介する。

* title()
    * '/'で入力されたキーワードをもとに前回のプログラムを走らせる
* get '/'
    * ユーザにキーワードを入力させる (formなどはindex.erb)
* post 'result'
    * title()の返り値を受け取り、結果を出力する (実際の画面はresult.erb)

なお、erbファイルは`views`ディレクトリに入れなければならないのに留意しなくてはならない。

index.erbのformに入力されたデータは`params`に格納され、`title()`に入力される。
その後、`title()`の返り値を`@narou_title`に代入することで、`result.erb()`に渡している。

```ruby:app.rb
require 'sinatra'
require 'sinatra/reloader'

def title(key1, key2, key3)
    keyword = [key1, key2, key3]
    keyword.delete("")
    split_titles = Marshal.load(open('split_titles.marshal', 'r'))
    noun_idxs = Marshal.load(open('noun_idxs.marshal', 'r'))

    idx = rand(1..split_titles.length)
    split_title = split_titles[idx]
    noun_idx = noun_idxs[idx]

    if keyword.length > noun_idx.length
        keyword = keyword.slice(0..noun_idx.length)
    end

    candidate = noun_idx.permutation(keyword.length)

    narou_titles = []
    for c in candidate do
        original = Marshal.load(Marshal.dump(split_title))
        c.each_with_index do |i, j|
            original[i] = keyword[j]
        end
        narou_titles.push(original.join)
    end

    if narou_titles.length < 1
        narou_titles = title(key1, key2, key3)
    end
    return narou_titles
end

get '/' do
  erb :index
end

post '/result' do
  narou_titles = title(params[:key1], params[:key2], params[:key3])
  @narou_titles = narou_titles
  erb :result
end
```

`index.erb`はerbファイルではあるが、Rubyの処理を用いていない。
formに関しては、下記の通りで`app.rb`との整合性をつけている。

```ruby:views/index.erb
<form action="result" method="POST">
  <div>
    key1:<input type="text" name="key1" value="">
  </div>
  <div>
    key2:<input type="text" name="key2" value="">
  </div>
  <div>
    key3:<input type="text" name="key3" value="">
  </div>
  <div>
    <input type="submit" value="生成する">
  </div>
</form>
```

`result.erb`は、`title()`で得られたタイトルの配列を表示している。
`app.rb`において`@narou_title`を設定しておき、ここで繰り返しを用いて表示している。

```ruby:views/result.erb
<div>
  <% @narou_titles.each do |title| %>
    <p><%= title %></p>
  <% end %>
</div>
```

## デプロイ
SinatraアプリのHerokuへのデプロイは下記の記事が詳しい。

[SINATRAアプリをHEROKUにデプロイして公開する](https://www.tech-ek.com/sinatra-deploy-to-heroku/)

ここでは、最小限の説明で一通りやらなければならないことを列挙する。
##### Procfileの作成
Herokuが動かすべき、処理を書いている。

```:Procfile
web: bundle exec rackup config.ru -p $PORT
```

##### config.ruの作成
Procfileにある処理を動かしたときに、Sinatraが動かすべきプログラムを書いている。

```:config.ru
require './app'

run Sinatra::Application
```

##### Git
Herokuにデプロイするのに、Gitは便利である。
ちなみにこれを`heroku create`する前にやっておかないと、
`git remote add heroku ほにゃらら.git`をするハメになる。

```bash:bash
git init
git add .
git commit -m "initial commit"
```

##### デプロイ
いよいよHeroku CLIにログインし、作ったアプリをデプロイしよう。
ただし、Heroku CLIはすでに導入済みとする。

```bash:bash
heroku login
heroku create
git push heroku master
```

一連のコマンドを実行したのち成功していれば、以下のコマンドによりブラウザ上で、自分のアプリがお披露目されるはずである。

```bash:bash
heroku open
```

## あとがき
いかがだったであろうか。
一発屋的思いつきプログラムをSinatraに組み込むのは比較的容易であった。
筆者の懸念する点は、

* cssを適用して、華やかな見た目にしようか
* 結果をOGP画像つきでTwitterシェア機能をつけようか

といった点である。

最後に[なろうパロディ☆タイトルジェネレータ](https://aqueous-wildwood-63896.herokuapp.com/)を使ってみて