11
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

絵文字 / EmojiAdvent Calendar 2016

Day 5

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

Posted at

はじめに

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

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

meme.gif
https://meme-ac.herokuapp.com/

環境

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

  • Mac
  • Ruby 2.3
  • Chrome

Somemoji

概要

r7kamura/somemojiは様々な種類のEmojiセットがシンプルで簡単なAPIで使えるようになるGemです。

詳しくはr7kamuraさんのブログを見てください :balloon:

現在は

  • Apple Emoji
  • EmojiOne
  • Noto Emoji
  • Twemoji

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

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

スクリーンショット 2016-12-03 14.38.37.png (91.9 kB)

使い方

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

$ gem install somemoji

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

次のように使えます。

# 変換前の文字列
>> 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の色々な情報を持っています。

>> 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コマンドが付属しています。次のようにして使います。

# 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を使います。

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

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

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

API設計

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

リクエスト

POST /markdown
パラメータ 内容
provider 文字列 Emojiセットの種類
markdown 文字列 Markdownテキスト

レスポンス

{
  "provider": "emoji_one", // Emojiセットの種類
  "rendered_html": "<h1>ほげほげ</h1>" // 描画されたHTML
}

準備

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

Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

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

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

Emojiダウンロード

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

$ 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を用意してください。

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でチャチャっと作ります。
ルーティング部分はこんな感じです。

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に変換してくれたりします。

こんな感じで使えます。

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_generatoremoj_namesというオプションを渡すことで、任意の変換ロジックを使うことができます。

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

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サーバのコードは次のようになります。

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にアクセスしてみてください。

$ bundle exec ruby api.rb

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

おわりに

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

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

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

11
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?