こんにちは。ペロリ開発部の bukuro です。
先日 Fasion Tech Meetup という Fashion x Technology をテーマとした勉強会の第二回を開催しました。 今回は iQON を運営する VASILY 様と FRIL を運営する Fablic 様との共同開催です。
イベント概要はこちら
弊社からは私 bukuro と編集チームが、開発と編集が一体となって MERY のかわいいを作っている話をしました。
また kazutoyo がアプリ UI のテストについて話をしました。
自分は今回発表の中で MERY Partner Program という外部のパートナーさんのコンテンツを MERY に掲載する仕組みについて話をしました。
パートナーさんのコンテンツは RSS の形式で配信され MERY の記事形式に変換されて掲載されます。
この記事では私が話した「開発と編集のちからでたくさんのかわいいを届けた話」 の中であまり触れることのできなかった外部コンテンツの MERY 記事変換に関する技術的な話をしようと思います。
変換の裏側
RSS で配信いただいたコンテンツの内容は HTML の形式で一度 MERY のデータベースに保存されます。 保存されたコンテンツのうち MERY ユーザに届けたい記事を編集が選定し、選定された記事を MERY の記事に変換してアプリに掲載します。 この変換処理は時間がかかることが想定されるため Sidekiq で非同期で処理され、完了すると Slack に通知がいく仕組みになっています。
Sidekiq を選んだ理由
Ruby での非同期処理は Resque と Sidekiq が有名ですが、今回は Sidekiq を選びました。 Resque はマルチプロセス、Sidekiq はマルチスレッドで動作するため、Sidekiq はプロセスを fork をすることによるオーバーヘッドが少なくて済むのが選んだ理由です。
また、今回共同でイベントを運営した VASILY 様が Resque と Sidekiq を比較した記事をブログに書かれていますのでそちらも読んでみてください。 http://tech.vasily.jp/entry/resque_to_sidekiq
変換処理
MERY の記事は以下のように アイテム の一次配列で構成されています。最終的には各パートナーさんのコンテンツもこの形式に変換されます。
# 記事データ ['見出しアイテム', '画像アイテム', 'テキストアイテム'...]
最初に、ネストされた構造データである HTML を Nokogiri を使って各 HTML 要素の一次配列へと変換します。
例えば以下のような HTML を
<html> <div> <h1>見出し</h1> <div> <a href="link">リンク</a> <text>テキスト</text> </div> </div> </html>
このような HTML 要素の一次配列へと変換します。
['<h1>見出し</h1>', '<a href="link">リンク</a>', '<text>テキスト</text>']
次にこの HTML 要素の配列を MERY の記事アイテムと一対一の関係を持った トークン(内部的に HTMLToken と呼ばれているもの)に変換をします。
['見出しtoken', '画像token', 'テキストtoken'...]
この変換には 状態遷移 の考え方を使っています。
状態遷移とは
ある 状態 に対して、特定の 入力 を行うと、決まった 処理 が行われて次の状態に 遷移 すること。
今回の場合だと HTMLToken の値を一つずつ入力値として受け取り状態を遷移させていきます。 状態としては以下の4つがあります。
- InDescription : テキストが続いている状態
- DescriptionOrLink : テキストもしくはリンクになり得る状態
- DescriptionBreak : 一度
<br>
をはさんだテキストが続いている状態 - None : その他
※ 参考図
それぞれの状態において次の HTML 要素の値によって状態が遷移し出力が行われます。
例えば InDescription の状態で <h1>
の HTML 要素が来た場合、テキストtoken と 見出しtoken が出力され状態が None
に遷移します。
つまり次に受け取る HTML 要素を入力値として受け取って HTMLToken を出力し新しい状態へと変化することで、 HTML の要素を MERY のアイテムと一対一の関係を持った HTMLToken へと作り変えています。
MERY はインラインのリンクに対応していないのですが、DescriptionOrLink という状態を持つことで例えば <a>
を受け取った時にそれがインラインのものなのか、独立してリンクとして扱っていいものなのかを次のタグを見て判断することができます。
また、DescriptionBreak という状態を持つことで改行を一つ含んだテキストを改行を挟んで同一のアイテムとして扱うことができます。
このように同じ種類の HTML 要素に対しても前後の要素によって出力を変更することができ、より柔軟にパートナーさんのコンテンツを表現することができます。
具体的な実装としては以下のようになっています。
それぞれの状態ごとにクラスが存在していて、初期状態は None クラスで与えられます。
繰り返し HTML 要素を受け取っては、現状の状態を元に HTMLToken の出力と次の状態の確定を行っています。
context = HtmlContext::None.new tokens = html_array.map do |element| context, tokens = context.next_context(element) tokens end
最後に HTMLToken を MERY のアイテムへと変換します。 ここはポリモーフィックに実装されています。
下のコードは実装の一部です。
HTMLToken はアイテム単位でクラスが実装されていて、tokens
はそれらのクラスのインスタンスの配列です。
このように各 HTMLToken のクラスには save_as_mery_item
という HTMLToken を MERY のアイテムとして保存するインスタンスメソッドが生えていて token
の属するクラスよって適した処理を行い保存します。
※ 実装
class HtmlToken::Headline < HtmlToken # HTMLToken を MERY のアイテムとして保存する def save_as_mery_item ... end end class HtmlToken::Description < HtmlToken def save_as_mery_item ... end end tokens.each do |token| token.save_as_mery_item end
まとめ
いかがでしたでしょうか? この記事では Fashion Tech Meetup ではお話できなかった MERY Partner Program のより技術的なお話をしました。
Fashion Tech Meetup ではこのようなテクニカルな話からもっとビジネスによった内容のものまで、各社の様々な取り組みを知ることが出来ます。 また、会の最後には懇親会の時間も用意されており参加者同士の悩みを共有したり、親睦を深める場となっています。
第三回目の Fashion Tech Meetup の開催も予定していますので、皆様ぜひぜひご参加ください。