Railsをバックエンドに持つReduxアプリを作ってみた(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

Railsをバックエンドに持つReduxアプリを作ってみた(翻訳)

ここ何か月もの間(大した期間ではないことはわかってますが)Flatiron Schoolでコーディングを学んでいましたが、同校のカリキュラムの最終プロジェクトで、私はReactとReduxをフロントエンドに、Ruby on Railsをバックエンドに使いました。

何しろ私にとって最初の大きなソロプロジェクトであり、しかもアプリ作成に使ったことのないReduxを使おうというのですから、相当頑張りましたが、間違いなく報われました。今やすっかりRedux信者です。

このアプリの目標は、自分の好きなテレビ番組をすべて保存して、放送時間やチャンネルを表示できるようにすることです。今は第二の「テレビ黄金期」ですので面白いテレビ番組が溢れかえっていますが、とても全部は追いきれません。番組の放送時間/チャンネル/系列ネットワーク/Webサイトやらなんやら、今週の新番組なのかどうかなどを忘れないようにするのは無理です。そこに登場するのが私のMyLineupアプリです。

デモ版アプリはこちらでご覧いただけます。デモ版はログイン不要で、データも入っていますので、ユーザー(そう、皆さんです)が簡単にアプリの機能を知ることができます。アプリのREADMEファイルはGitHubリポジトリでご覧いただけます。

バックエンド

アプリのバックエンドはRuby on Railsで作成しました(GitHubリポジトリ)。バックエンドの責務はデータベースがらみのクエリだけなので、セットアップはかなり簡単でした。また、TVデータの取得に2種類の異なるAPI(trakt.tv APITVmaze API)を使いました。パフォーマンス監視については、NewRelicでバックエンドを最適化しました。

作ったモデルは、UserShowEpisodeUserShowUserEpisodeです。

  • Userはだいたいこうなるだろうという感じです。
class User < ApplicationRecord
  has_many :user_shows
  has_many :shows, through: :user_shows
  has_many :user_episodes
  has_many :episodes, through: :user_episodes
  has_secure_password
end
  • Show
class Show < ApplicationRecord
  has_many :user_shows, dependent: :destroy
  has_many :users, through: :user_shows
  has_many :episodes
end
  • Episode
class Episode < ApplicationRecord
  has_many :user_episodes, dependent: :destroy
  has_many :users, through: :user_episodes
  belongs_to :show
end

UserShowUserEpisodeはそれぞれ、UserShowのjoinテーブルと、UserEpisodeのjoinテーブルです。

class UserShow < ApplicationRecord
  belongs_to :user
  belongs_to :show
end
class UserEpisode < ApplicationRecord
  belongs_to :user
  belongs_to :episode
end

作成したモデルでは、ユーザーが番組を評価できます。また、ユーザーが番組やエピソードを削除したときにデータベースから削除しないようになっています。

モデルごとに、ユーザーのCRUD操作のためのコントローラも必要です。Showsコントローラは、表示のCRUDアクションの他に、外部APIへのリクエストのフェッチCRUDアクションも扱うので、責務は最も大きくなりました。

フロントエンド

上述のとおり、私はフロントエンドを(自分がよく知っている)Reactと、(学び始めて数日しか経っていない)Reduxで作りました。Reactのコンポーネントやステートの構造は快適でしたが、Reduxではまごついてしまいました。しかし私は挑戦せずにはいられない男なので、Reduxを試してみたかったのです。そしてReduxは私にとってかなりよかったことを申し添えなくてはなりません(フロントエンドのリポジトリはこちら)。パフォーマンスの監視にはChrome Performance Dev Toolsを使いました。

ダッシュボード

ログインすると、ユーザーは自分の「Dashboard」にリダイレクトされます。Dashboardには、その日の夜の「ラインナップ」や、その日の夜のおすすめ番組が表示されます。

「My Lineup」に表示されるのは、Reduxストアの「myLineup」から取って来た、その日に放映されるエピソードです。「Other Shows to Watch」には、TVmaze APIから取ってきた情報(その日に放映されるあらゆる番組を取れます)を表示します。リストを使いやすくするために、レーティングが8を超え、かつ「User’s Lineup」にまだない番組だけをフィルタで表示しました。

this.props.onTonight.filter(episode => episode.show.rating.average > 8 && !ids.includes(episode.id))

「Dashboard」を含むコンポーネントをステートフルにしなければなりませんでした。これはReduxの「真の単一情報源」とは逆を行くものですが、よいきっかけでした。これによって結果をページネーションしてユーザーがエピソードのリストをスクロールできるようになりました。フィルタされた結果を分割して、表示するステートに5つずつ保存しました。「Older」をクリックすると、フィルタされた結果に直前の5つの項目を単に表示します。このあたりを手伝ってもらったLindsey Wellsへのリンクを貼っておきます。

「My Lineup」

このプロジェクトで(少なくとも私にとっては)最も重要な部分は、言うまでもなく、ユーザーのラインナップを表示するカレンダーです。番組がいつどの局で放映されるかをカレンダーで正確に知ることができます(サンプルアプリ)。

ここにある番組を片っ端から見る時間があったらよいのにと思います。

ユーザーの番組をすべて表示するのに使ったのはReact Big Calendarです。RBCの使い勝手はそれほどよくありませんが、これを使ってカレンダーのラインナップにユーザーのエピソードをすべて表示することに成功しました。時刻のフォーマットや設定にはMoment.jsを使いました。

ユーザーは、その日の番組や一週間分の番組を知ることができます。カレンダーを進めたり遡ったりもできます。保存した来月分の全番組の概要も表示できます(そこまでして予定を押さえておきたい場合に備えて)。

ラインナップからエピソードを削除することも、エピソードの詳細をモーダルポップアップに表示することもできます。

ページの最下部にはその夜のおすすめ番組が表示され、番組をクリックするとモーダルポップアップでラインアップに直接追加できます。

プレミアカレンダー

Premieresカレンダーは、その週のプレミア(シリーズの最初のエピソード)をすべて表示できるお楽しみ機能です。

これも同様に、1日分または1週間分を表示できます。trakt.tv API から取得した新作番組をフィルタして、レーティングの高い番組だけを表示しています。さらに、「エピソード1」や「シーズン1、エピソード1」の違いがわかるようにして、その番組がTotal Divasのようにシーズンの続きなのか、S.W.A.T.のようにまったくの第1回なのかがわかるようにしました。

これもモーダルポップアップで追加したり番組情報を表示したりできます。

トレンド/高視聴率

これはtrakt.tv APIの楽しい機能です。番組のその瞬間のトレンド(APIでは1時間おきに更新)や、最も視聴率の高い番組を表示でき、時間でフィルタすることもできます。

このユーザーインターフェイスにはSemantic UIを使いました。

検索

検索では、TVmaze APIを呼び出して結果を表示します。表示された番組を追加して番組リストにリダイレクトできます。

ShowsページとShowページ

最後はShowsページとShowページです。Showsページにはユーザーが保存したTV番組がすべて表示されます。ユーザーが番組をクリックすると、番組の個別のページにリダイレクトされます。

個別のページには、ユーザーが選択した番組の情報と、番組の全エピソード(上述のページネーション機能を利用)、およびその番組に関連するおすすめ番組が表示されます。

おすすめ番組はtrakt.tv APIから取り出します。ユーザーがまだ視聴していない番組だけを表示します。

この星はアプリのユーザーが付けたものです。それ以外の情報はすべてTVmaze APIからのものです。

アプリの紹介は以上です。アプリ作成はとても楽しく、今もいくつかの機能を拡張してみたいと思っているほどです。特に、同じ番組を保存した他のユーザーをEpisodeページで表示できるようにしてSNS的な要素を加えたいと思います。今後どうなるかはいずれわかるでしょう。

関連記事

JavaScript: Reduxが必要なとき/不要なとき(翻訳)

Reduxストアの概念をRubyで再実装して理解する(翻訳)

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833 コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。 これまでにRuby on Rails チュートリアル第2版の半分ほど、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れてそれぞれ一部を翻訳。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 実は最近Go言語が好き。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ