| @ブログ

多分自分しか見てなくて月間の PV も 10 しかない Archives ページで、これまで年を選択していない場合は直近一年間の記事だけを表示するようにしていたのを全期間表示するようにした。このブログは 1300 記事くらいあって、全部一気に取得するとそれなりにレスポンスが遅くなる( 2 秒弱かかる)。待ってる時間が少々長くなるのでローディングスピナーを表示するようにしてみた。

Loading Spinner

結構いい感じで気に入っている。使ったのは以下。色の変更なども簡単だった。

全期間の記事が表示されるとカテゴリーごとに絞り込んだときにそのカテゴリーで通算何件くらい記事を書いているのかが確認しやすくなってとてもよい。完全に自己満足のサイト改善。

| @技術/プログラミング

Archive ページ をリファクタリングした。

これまで gulp をビルドに利用していた(Archive ページを React Router 化)が、 webpack を使うように変えた。

React のコードも見直して、 DOM の状態に依存して表示・非表示を切り替えるコードがあったりして( 🐙 Archive ページにカテゴリごとの記事件数を表示する機能を追加 )ごちゃごちゃしてたので DOM を直接ごちゃごちゃ操作するのをやめて React で管理するように変えた。親コンポーネント、子コンポーネント、孫コンポーネント、子コンポーネントの兄弟コンポーネント間で状態を共有する必要があって、結構難儀した。

Archives React Component 1.png

実際の画面を見るとこんな感じ。

Archives React Component 2.png

App というコンポーネントがルートにあって、子に CategoryListCategoryList の子コンポーネント( App からすると孫)に Category コンポーネントがある。記事一覧自体は CategoryList と兄弟コンポーネントである Archive コンポーネントが担当している。

Archives React Component 3.png

こんな感じで特定の Category が選ばれたことを Category のクリックイベントをトリガーに CategoryList に伝達し、 CategoryList はさらにそれを App コンポーネントに伝える。その結果が App から Archive コンポーネントに伝えられ、表示内容が変更される。

この辺を参考にして実装した。コールバック関数を props として引き回し、状態を回収する感じ。

ただこういう込み入った状態の管理を React で行う場合は Redux などを利用するのが良いようだった。

前職のとき、 Redux とか Flux が出てきた頃に F/E のエンジニアの人たちが熱狂してたけど自分はいまいち理解できなくて、傍観するだけだったが、いまさらにして何となく Flux アーキテクチャの概要的なものを把握することができた気がする。ただ自分の場合は深みに入り込まず極力シンプルに作りたいと思っていたので Redux などには手を出さず、 Callback で愚直に状態を親コンポーネントに伝達していく方法をとった。

React 、やっぱり大分良いものだとという感じがした。 jQuery でクラスや CSS で show - hide を Toggle していた頃とは隔世の感がある。

| @技術/プログラミング

Archive ページにカテゴリごとの記事件数を表示する機能を追加した。

API でカテゴリごとの記事件数を返すのではなく、 JavaScript だけで entries.length を数えるようにしている。 React で関連コンポーネントが描画されたあとにいい感じに数える処理をトリガーする方法が分からず、描画完了後 1000msec 以内で 100msec ごとに記事数を数える処理を実行している。あまり賢い実装方法ではないと思うけど、一番記事数が多い 2006 年でも 600msec くらいで Ajax のレスポンスは返ってくるのでまぁ実用上問題はない。

こうして見ると、 2011 年から一切映画についてブログに書いてないことが分かる。さすがに年に一本も映画を見ないということはなくて、年に二、三本は絶対見ていると思う。以前は基本的に映画館で見た映画の感想を書くようにしていたので、映画館に行かなくなった(行けなくなった)せいで映画の記事を書かなくなったんだろうなぁと思う。

自分のブログに機能を追加することでここ数年の生活の傾向が分かるのが面白い。実は昨日、 Sunset Live の記事を書いたのも音楽について全然書いてないことに気づいたからなのであった。

| @技術/プログラミング

Archive ページに React Router を導入して、年を切り換えたときにページ遷移なしで表示を切り替えられるようにした。あわせてカテゴリ一覧を表示して記事を絞り込み表示するようにした。

React はこれまで cdnjs からダウンロードしていて JSX の変換は Babel の browser.js を使ってブラウザーで行っていたけど JS の開発環境もちょびっとだけモダンにして npm モジュールをインストールして手元でいろいろやるようにした。一枚のスクリプトに書いていたコードはコンポーネントごとに分割して browserify で連結するようにした。 React を自前で配信するとファイルサイズも結構無視できない感じになったので minify なんかもするようにした。

F/E の情報収集、きらきらネームが多くて調べるのにかなり時間がかかった。 npm モジュールの名前とそれが何をやるためのものなのかがすぐに分からない。じいちゃんばあちゃんが横文字を覚えられないのに似てる。 Ruby もきらきらネーム gem は多いけど寿命が長いのである程度触ってれば覚えられる。 JS はライブラリの寿命が 1 年くらいだから雨後のたけのこのようにどんどん新しいやつが出てきて全然覚えられない。自分が高齢化してきてるだけで若い人だったらすんなり頭に入ってくるんだろうか。

| @技術/プログラミング

Archives ページは部分的に React.js を使ってる。これを全部 React にして SPA 化してみたい。そのためには Server Side Rendering が必要。

Ruby で Server Side Rendering するライブラリでは react-rails が有名だけど、 Rails 以外で Server Side Rendering したい。

有名なやつでは airbnb の hypernova というのがあった。これ自体は Node.js で、 hypernova-ruby というクライアントからサーバーを呼び出して Server Side Rendering するというもの。なのでサーバー側に一個プロセスが増える。開発環境でも Node.js でプロセス起動したりしないといけない。仕事で Microservice には食傷気味になってきてるので趣味では Majestic Monolith™ で行きたい。

名前似てるけど Hyper-Ract というやつもあった。これは過激で JavaScript とか HTML を排除して Ruby で全部書こうぜという Opal が React 対応したもの。良いんだけど F/E の進化早すぎ問題に対応できなくなる気がする。数年後には CoffeeScript みたいになりそうな予感がある。スタンダードでシンプルなものを使いたい。

もう一個、スター全然付いてないしメンテナンスもされてなさそうなんだけど、 V8 で React 読み込んで出力するだけという簡単なのもあった。react-ruby-v8 という直球な名前。こんなので十分かもしれない。

| @労働

Kaizen Chat とは

Kaizen Platform 内でユーザー同士がコミュニケーションを取ることができるサービス。

  • Kaizen Platform のユーザー
    • カスタマー
      Kaizen Platform と契約し、 A/B テストツールや Growth Hacker によるサイト改善のデザイン案を募集
    • Growth Hacker
      募集に応じてカスタマーサイトのデザインを改善するデザイン案を投稿
    • Kaizen Platform 社員
      カスタマーと Growth Hacker の間の調整役

Kaizen Platform 内のユーザーが外部のツールや電話を利用して行っていた伝言ゲーム的なコミュニケーションを置き換えて、直接コミュニケーションを取ってもらうようになることが目標。

サーバーサイド 2.5 人、フロントエンド 2 人で 2 ヶ月くらいで作った。

構成

数多く存在しているマイクロサービスの中の一つとして実装。

フロントエンド

  • React
  • 多分他にもバズワード的な仕組み・フレームワークを使ってる(後で調べて書きます)

バックエンド

  • Ruby 2.3
  • Rails 5
  • MySQL 5.7
  • Sidekiq
  • Redis
  • Pusher (SaaS)

Rails 5 で開発

Rails 5 は Release Candidate だったが利用することに。

リリース直後に Rails 5 出て将来的に Rails 4 から Rails 5 へのアップデートにリソースを割くの避けたかった。(別の Rails アプリを 3 から 4 に上げたときには大変だった…)

Rails 5 で開発して困ったこと

あんまりないが強いて挙げるとすれば以下。

  • 個人的に好きで前職の頃から使ってた gem ( モデル層のバリデーションのテスト書くのが楽になる accept_values_for ) が Rails 5 対応してなかったので Pull Request 送ったりなど
  • ActiveResorces など Rails 5 対応が遅かった gem もあったが github 直接参照して対応版がリリースされたら rubygems.org を見るように変えるなど(普通です)
  • 社内の Rails プロジェクトで共通して使ってる gem を Rails 5 対応させるなど

WebSocket は Pusher (SaaS) を利用

Pusher を使った。 ActionCable 使って自前実装するのは考えなかった。

  • Rails 5 なら ActionCable では????
    • 急なアクセス増などを考えて SaaS を使うことに
    • WebSocket に関してはインフラのことを考えなくてよくなる
    • 無理に自前実装せず、少々金がかかったとしても、外のリソースを利用できるときは SaaS を使う(社風)
      大人の事情で使えない、とかがないのがよい
  • フロントエンド側も Pusher から SDK が提供されており楽できた(はず)

Pusher との通信の詳細

  • CRUD のうち Read だけ Pusher 経由で行う
  • Pusher は Create/Update/Delete も担えるけど Rails アプリと Pusher とクライアントの間のデータの流れを一方向にしたかった

Kaizen Chat Pusher

感想

Sidekiq 速い

  • 社内で初めて Sidekiq を導入したけど速かった
  • Thread で並行処理をするのでスレッドセーフな作りにしないといけない(利用 gem 含む)
  • capistrano-sidekiq が sidekiq 本体と機能かぶってるところがあるのがちょっと残念

マイクロサービス間の通信が課題

  • マイクロサービス間でなるべく疎結合になるように、相手のサービス側の DB を直接参照しないように頑張る(気合い)
  • スピードが遅くなってしまうところはキャッシュする

BFF 的な考え方必要

  • ActiveModelSerializers 使ってると Serializer が乱立して収集付かなくなる
  • Frontend から必要なフィールド渡してもらってそれをシュバッと返すおシャンティな API にしていきたい

今後

  • モバイルクライアントの開発
    チャットなのに携帯電話で返信できないとかダメですよね…
  • 通知の充実
    チャットなのに通知こなくて気がつけないとかダメですよね…
  • 画像ハンドリングの充実
    負荷やジョブとの戦いに突入します。

Kaizen はチャットの会社じゃないので、自分たちがチャットの機能を作ることはどんな意味があるのか、ということを考えながら機能追加していきたいですね。

なお Kaizen Chat は Kaizen Platform にアカウントをお持ちの方でしたらどなた様でも無料でご利用いただけますのでもしよかったら触ってみてください。僕にファンレターを送ることも出来ます。

また Kaizen Platform, Inc. は本日( 2016-09-08 )から開催されている RubyKaigi 2016 にブースを出しておりますので CTO (リクルート時代に chouseisan 作ってた)やエンジニアと話してみたい人、 Kaizen Platform ロゴ入り手ぬぐいが欲しい人、僕のアイコンのステッカーが欲しい人はお気軽にお越しください。

| @技術/プログラミング

このブログの Archive ページ自分で作った Lokka Plugin でできているのだけど、ここを React を使って作ってみた。 React チュートリアルの写経の題材を自分のブログにした感じ。 CoffeeScript オワコンと言われて久しいけど Coffee で書いた。 JSX を CoffeeScript で書くときはバッククオートで囲むとよいという知見が得られた。

Entry = React.createClass
  render: ->
    `(
      <li className="entry">
        <a href={this.props.link}>{this.props.title}</a>
        <div className="detail-information">
          <span className="created_at">{this.props.created_at}</span>
          <Category category={this.props.category} />
        </div>
      </li>
    )`