はじめに
複数のEmojiセットを簡単に扱うことができるSomemojiというRuby Gemを紹介します。
また、それを使って好きなEmojiセットを選択することができるMarkdownエディタをRuby/HTML/JavaScriptで作ってみます。
完成品はこんな感じになります
https://meme-ac.herokuapp.com/
環境
だいたいこんな感じの環境を想定しています。
- Mac
- Ruby 2.3
- Chrome
Somemoji
概要
r7kamura/somemojiは様々な種類のEmojiセットがシンプルで簡単なAPIで使えるようになるGemです。
詳しくはr7kamuraさんのブログを見てください
現在は
- Apple Emoji
- EmojiOne
- Noto Emoji
- Twemoji
の4種類に対応しています。
Somemojiを使えばSlackのEmoji選択みたいな機能が簡単に実現できるようになります
使い方
なにはともあれインストールしておきましょう。
$ 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を用意してください。
# 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を用意してください。
<!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でチャチャっと作ります。
ルーティング部分はこんな感じです。
# 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_generator
とemoj_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サーバのコードは次のようになります。
# 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を書いて、右側に内容が表示されれば成功です
Emojiセットを選択すれば右側のEmojiも変わると思います
おわりに
なんとこれだけで色々なEmojiセットが使えるMarkdownエディタができてしまいました
Somemoji素晴らしいです
Emoji界隈のエコシステムも充実してきて、ますますEmojiの今後が楽しみですね
-
somemoji extract
(もっというと内部で使ってるgemoji)のApple Emoji抽出がSierraにしか対応していない ↩