SomemojiでマルチEmojiなMarkdownエディタを作って遊ぶ

# はじめに
複数のEmojiセットを簡単に扱うことができるSomemojiというRuby Gemを紹介します。
また、それを使って好きなEmojiセットを選択することができるMarkdownエディタをRuby/HTML/JavaScriptで作ってみます。

完成品はこんな感じになります :sparkles:

![meme.gif](https://qiita-image-store.s3.amazonaws.com/0/43208/ad5772b2-4282-ad0e-b052-0e31d672d607.gif)
https://meme-ac.herokuapp.com/

# 環境
だいたいこんな感じの環境を想定しています。

* Mac
* Ruby 2.3
* Chrome

# Somemoji
## 概要
[r7kamura/somemoji](https://github.com/r7kamura/somemoji)は様々な種類のEmojiセットがシンプルで簡単なAPIで使えるようになるGemです。

詳しくは[r7kamuraさんのブログ](http://r7kamura.hatenablog.com/entry/2016/10/09/135444)を見てください :balloon: 

現在は

* Apple Emoji
* EmojiOne
* Noto Emoji
* Twemoji

の4種類に対応しています。

Somemojiを使えばSlackのEmoji選択みたいな機能が簡単に実現できるようになります :blush: 

<img width="395" alt="スクリーンショット 2016-12-03 14.38.37.png (91.9 kB)" src="https://img.esa.io/uploads/production/attachments/1679/2016/12/03/4429/662551ab-2bd1-4d7e-a1db-c1c22fe0c4de.png">

## 使い方
なにはともあれインストールしておきましょう。

```bash
$ gem install somemoji
```

Somemojiでは、Emojiコード(`:poop:`みたいなの)か、生のEmojiから画像のURLに変換することができます。

次のように使えます。

```rb
# 変換前の文字列
>> str = "I have a 🖊. I have an :apple:"
=> "I have a 🖊. I have an :apple:"

# Somemoji::EmojiCollectionオブジェクトを生成する。
>> emoji_collection = Somemoji.twemoji_emoji_collection
=> #<Somemoji::EmojiCollection:0x007feb511ce2f8>

# Emojiコードをimgタグに変換する
>> emoji_collection.replace_code(str) { |emoji| %(<img alt="#{emoji.character}" src="/assets/emoji/twemoji/#{emoji.base_path}.png">) }
=> "I have a 🖊. I have an <img alt=\"🍎\" src=\"/assets/emoji/twemoji/unicode/1f34e.png\">"

# Emojiをimgタグに変換する
>> emoji_collection.replace_character(str) { |emoji| %(<img alt="#{emoji.character}" src="/assets/emoji/twemoji/#{emoji.base_path}.png">) }
=> "I have a <img alt=\"🖊\" src=\"/assets/emoji/twemoji/unicode/1f58a.png\">. I have an :apple:"
```

`emoji`というブロック変数には`Somemoji::Emoji`オブジェクトが入り、このオブジェクトがそのEmojiの色々な情報を持っています。

```rb
>> emoji = emoji_collection.find_by_code("apple")
=> #<Somemoji::Emoji:0x007feb53ae21f8>
>> emoji.code
=> "apple"
>> emoji.category
=> "food"
>> emoji.keywords
=> ["apple", "core", "creationism", "doctor", "electronics", "food", "fruit", "mac", "red", "school", "teacher"]
>> emoji.base_path
=> "unicode/1f34e"
```

`Somemoji::Emoji#base_path`では画像ファイルのパスが得られます。Somemojiにはこのパスに画像ファイルを自動ダウンロードするための`somemoji`コマンドが付属しています。次のようにして使います。

```bash
# Apple Emojiをダウンロードして、./assets/emoji/apple/ 以下に保存する
$ somemoji extract --provider=apple --destination=./assets/emoji/apple

# EmojiOneをSVGでダウンロードする
$ somemoji extract --provider=emoji_one --format svg --destination=./assets/emoji/emoji_one 
```

# Markdownエディタを作る
## 概要
Somemojiがなんとなくわかったところで、マルチEmojiなMarkdownエディタを作っていきます。

ブラウザで入力したMarkdownをサーバに送信し、サーバでHTMLを描画して返すという単純な仕組みのものです。

サーバサイドはRuby、Sinatraで作ります。
Markdownの変換には[increments/qiita-markdown](https://github.com/increments/qiita-markdown)を使います。

フロントエンドはHTMLとJavaScriptで作ります。

ソースコードはこちらにあります。
https://github.com/nownabe/meme

今回は諸事情でApple Emojiは抜いてあります。[^1]

[^1]: `somemoji extract` (もっというと内部で使ってるgemoji)のApple Emoji抽出がSierraにしか対応していない

## API設計
今回JavaScriptでMarkdownをサーバに送信し、レスポンスを受け取ります。
APIは次のようにします。

### リクエスト
```
POST /markdown
```

| パラメータ | 型 | 内容 |
| --- | --- | --- |
| `provider` | 文字列 | Emojiセットの種類 |
| `markdown` | 文字列 | Markdownテキスト |

### レスポンス
```js
{
  "provider": "emoji_one", // Emojiセットの種類
  "rendered_html": "<h1>ほげほげ</h1>" // 描画されたHTML
}
```

## 準備
適当なディレクトリを作って、Gemfileを用意してください。

```rb:Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

gem "qiita-markdown"
gem "somemoji"
gem "sinatra"
```

`bundle install`もしておきましょう。

## Emojiダウンロード
Emojiセットをダウンロードしておきます。時間がかかるので、コマンド打って次の作業に移ってしまってください。

```bash
$ bundle exec somemoji extract --provider emoji_one --destination ./public/images/emoji/emoji_one
$ bundle exec somemoji extract --provider noto --destination ./public/images/emoji/noto
$ bundle exec somemoji extract --provider twemoji --destination ./public/images/emoji/twemoji
```

## フロントエンド
`public/index.html`にこんな感じのHTMLを用意してください。

```html:public/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Multi Emoji Markdown Editor</title>
    <script>
      setInterval(function() {
        var provider = document.getElementById("provider").value
        var markdown = document.getElementById("editor").value
        var formData = new FormData()
        formData.append("provider", provider)
        formData.append("markdown", markdown)
        fetch(
          "/markdown",
          { method: "POST", body: formData }
        ).then(function(response) {
          return response.json()
        }).then(function(response) {
          document.getElementById("view").innerHTML = response.rendered_html
        })
      }, 1000)
    </script>
  </head>
  <body>
    <div>
      <select id="provider">
        <option value="emoji_one">EmojiOne</option>
        <option value="noto">Noto</option>
        <option value="twemoji">Twemoji</option>
      </select>
    </div>
    <div style="display: flex">
      <textarea id="editor" style="width: 50%; height: 800px;"></textarea>
      <div id="view" style="width: 50%; padding: 10px">View</div>
    </div>
  </body>
</html>
```

1秒ごとに`textarea#editor`の内容をサーバに送信して、返ってきたHTMLを`div#view`に突っ込んでいるだけです。

## APIサーバ 1
APIサーバはSinatraでチャチャっと作ります。
ルーティング部分はこんな感じです。

```rb:api.rb
# frozen_string_literal: true

require "json"
require "sinatra"

post "/markdown" do
  content_type :json
  {
    provider: params[:provider],
    rendered_html: Renderer.new(params[:provider]).render(params[:markdown])
  }.to_json
end
```

`Renderer.new(provider).render(markdown)`で、Markdownを指定されたEmojiセットで描画するような`Renderer`クラスを作ります。

## qiita-markdown
`Renderer`クラスを実装する前に、qiita-markdownを少しだけ説明します。
qiita-markdownはIncrementsによるオープンソースのMarkdownプロセッサです。MarkdownをパースしてHTMLに変換してくれたりします。

こんな感じで使えます。

```rb
require "qiita/markdown"
processor = Qiita::Markdown::Processor.new(hostname: "localhost:4567")
result = processor.call("# ほげ")
result[:output].to_s
#=> "\n<h1>\n<span id=\"ほげ\" class=\"fragment\"></span><a href=\"#%E3%81%BB%E3%81%92\"><i class=\"fa fa-link\"></i></a>ほ げ</h1>\n"
```

qiita-markdownはデフォルトでEmojiを変換してくれますが、`emoji_url_generator`と`emoj_names`というオプションを渡すことで、任意の変換ロジックを使うことができます。

`emoji_names`にはEmojiコードのリスト、`emoji_url_generator`にはEmojiコードを受けて文字列としてURLを返すProcなどを渡すことになります。Somemojiを使う場合は次のようになります。

```rb
emoji_collection = Somemoji.twemoji_emoji_collection
processor =
  Qiita::Markdown::Processor.new(
    hostname: "localhost:4567",
    emoji_url_generator: proc { |code| "/images/emoji/twemoji/#{emoji_collection.find_by_code(code).base_path}.png" },
    emoji_names: emoji_collection.codes
  )
result = processor.call("# ほげ\n:poop:")
result[:output].to_s
=> "\n<h1>\n<span id=\"ほげ\" class=\"fragment\"></span><a href=\"#%E3%81%BB%E3%81%92\"><i class=\"fa fa-link\"></i></a>ほ げ</h1>\n\n<p><img class=\"emoji\" title=\":poop:\" alt=\":poop:\" src=\"/images/emoji/twemoji/unicode/1f4a9.png\" height=\"20\" width=\"20\" align=\"absmiddle\"></p>\n"
```

## APIサーバ 2
以上を踏まえ、最終的なAPIサーバのコードは次のようになります。

```rb:api.rb
# frozen_string_literal: true

require "json"
require "sinatra"
require "somemoji"
require "qiita/markdown"

class Renderer
  def initialize(provider)
    @provider = provider
    @processor =
      Qiita::Markdown::Processor.new(
        hostname: "localhost:4567",
        emoji_url_generator: emoji_url_generator,
        emoji_names: emoji_names
      )
  end

  def render(markdown)
    @processor.call(markdown)[:output].to_s
  end

  private

  def emoji_collection
    @emoji_collection ||=
      case @provider
      when "emoji_one"
        Somemoji.emoji_one_emoji_collection
      when "noto"
        Somemoji.noto_emoji_collection
      when "twemoji"
        Somemoji.twemoji_emoji_collection
      end
  end

  def emoji_names
    emoji_collection.codes
  end

  def emoji_path(code)
    "/images/emoji/#{@provider}/#{emoji_collection.find_by_code(code).base_path}.png"
  end

  def emoji_url_generator
    proc { |code| emoji_path(code) }
  end
end

post "/markdown" do
  content_type :json
  {
    provider: params[:provider],
    rendered_html: Renderer.new(params[:provider]).render(params[:markdown])
  }.to_json
end
```

## 起動
次のコマンドでサーバを起動して、`http://localhost:4567/index.html`にアクセスしてみてください。

```bash
$ bundle exec ruby api.rb
```

テキストエリアにMarkdownを書いて、右側に内容が表示されれば成功です :tada:
Emojiセットを選択すれば右側のEmojiも変わると思います :blossom: 

# おわりに
なんとこれだけで色々なEmojiセットが使えるMarkdownエディタができてしまいました :flushed:
Somemoji素晴らしいです :heart: 

Emoji界隈のエコシステムも充実してきて、ますますEmojiの今後が楽しみですね :laughing: