読者です 読者をやめる 読者になる 読者になる

クラウドワークス エンジニアブログ

日本最大級のクラウドソーシング「クラウドワークス」の開発の裏側をお届けするエンジニアブログ

日報をemojiサジェストで楽しくした話

こんにちは!最近子供が生まれたRailsエンジニアの鈴木( @suzan2go )です。

クラウドソーシングのクラウドワークスでは日報文化が定着しており、エンジニアだけではなく営業やコーポレートの方たちも毎日日報を書いて全社に共有しています。日報といえばesa.ioQiita:team といった素晴らしいプロダクトがありますが、クラウドワークスではDirectoryというマークダウンで日報を書いて共有できるサービスを社内向けに公開していて、エンジニアが業務の合間の気分転換に、あるいは新技術のお試しの場として開発をしています。

f:id:suzan2go:20160120165533p:plain

今回は、このDirectoryに「GitHub, Qiita Likeなemojiサジェスト機能」を実装したお話です。

なぜやったのか?

GitHubやQiitaには: と打つと候補のemojiをサジェストしてくれる機能がありますよね? f:id:suzan2go:20160120190140p:plain

Directoryには長いことその機能がなかったため、普段からマークダウンを書くエンジニア以外の方にはあまりemojiが使われていませんでした。

普段マークダウンを書かない人でも気軽にemojiを使って投稿できれば、より楽しく日報をかけるはず!と思いたちカッとなって自分でemojiサジェスト機能を実装してみました。

どうやったのか

以下のOSSを使って実装しました。

  • jquery-textcomplete (Qiitaのemojiサジェストにも使われているOSS!)
  • gemoji (マークダウンでemojiをrenderするために必要なOSS!)

jquery-textcompleteのデモページにemojiのサジェスト機能のサンプルはありますが、これをRailsでやる場合はemojiの画像ファイルにつくdigestの値を考慮する必要があります。

色々方法は有ると思いますが、今回はemoji名で検索してヒットしたemoji名とemojiの画像パスを返すAPIを作ってサクッと対応してみました。

gemoji

gemojiとはGitHubが提供する、emoji名とそれに紐づくemojiのimageファイルを管理するライブラリです。Directoryでも使用しているマークダウンプロセッサのqiita-markdownでは、gemoijを内部で使っていてマークダウン上の :sushi:f:id:suzan2go:20160120165940p:plainのような画像イメージに変換しているというわけです。 *1

gemojiにはActiveRecordでいうfind系のメソッドは提供されているのですが、whereなメソッドは提供されていません。ですので今回やりたいLike検索的な機能は自前で作る必要があります。

そこでgemojiが提供するEmoji::Characterクラスには生えている、namealiasesというメソッドを使って検索するメソッドを拡張してみます。

# たとえば :+1: だったらnameは+1、aliasesは ["+1", "thumbsup"]といった感じです。
# ちなみにnameはaliases.firstのaliasだったりします。
module Emoji
  def start_with(name)
    emojis = Emoji.all.select do |emoji|
      emoji.aliases.any? do |_alias|
        _alias =~ /^#{name}/
      end
    end
    emojis.sort_by{ |e| e.name }
  end
end

できた!

pry(main)> Emoji.start_with("smile")
=> [#<Emoji::Character:smile(1f604)>, #<Emoji::Character:smile_cat(1f638)>, #<Emoji::Character:smiley(1f603)>, #<Emoji::Character:smiley_cat(1f63a)>]

API側はこんな感じになります。 emojiの名前と、pathを返すJSONを返します。

class Api::EmojisController < ApplicationController
  EMOJI_NUM_LIMIT = 10
  def index
    @emojis = Emoji.start_with(params[:query]).first(EMOJI_NUM_LIMIT)
  end
end
json.array!(@emojis) do |emoji|
  json.value emoji.name
  json.url image_path("emoji/#{emoji.image_filename}")
end

jquery-textcomplete

ほぼサンプルコードそのままな感じですが、先ほど作ったAPIに対してリクエストを投げて、返って来たjsonで候補を作ってあげます。

    $(this.refs.textarea).textcomplete([
      {
        match: /\B:([\-+\w]*)$/,
        search(term, callback) {
          return $.getJSON("/api/emojis", {
            query: term
          })
          .done( (data) => callback(data) )
          .fail( () => callback([]) )
        },
        template(data) {
          return `<img src="${data.url}" class="emoji" /> ${data.value}`;
        },
        replace(data) {
          return `:${data.value}:`;
        },
        index: 1
      }
    ])

完成

f:id:suzan2go:20160120171818g:plain できた!

今回APIやJavaScriptはQiitaの実装をすごーく参考にさせていただきましたf:id:suzan2go:20160120173004p:plain

普段何気なく使っているものでも、実際に自分で作ってみると理解が深まりますね!

最後に

クラウドワークスではユーザー指向でサービス改善していきたいエンジニアを募集しています!

www.wantedly.com

*1:正確にはqiita-markdownが使っているHTML::PipelineのEmojiフィルタがgemojiを使っています。

© 2016 CrowdWorks, Inc., All rights reserved.