| @ブログ

普段は Vim などのテキストエディターで文章を書いていて、ブログの投稿画面にはできあがった内容をコピペするだけだったので、投稿画面の使いやすさやは気にしたことがなかった。画像のドラッグ・アンド・ドロップ・アップロードや、オン・ザ・フライでプレビューをできるようにしたが、テキスト自体の書きやすさを改善しようとはしてこなかった。

大筋はテキストエディターで書いたとしても、最後に推敲したり、推敲していて気が付いたおかしなところの修正は投稿画面で行うことが多い。やっぱり投稿画面の書きやすさは重要だ。特に長大な内容を編集しているときにテキストエリアが狭いととても使いづらい。ある程度大きさがあり、画面内に大量の文字が表示されるテキストエリアが好みだ。

Lokka が開発されていた 10 年以上前は解像度の小さいディスプレイが主流で、 Lokka の管理画面は小さいサイズのウィンドウで閲覧することを想定したテキストエリアのサイズになっている。今日の高解像度ディスプレイで見ると不便なので画面一杯にテキストエリアが広がるようにした。

投稿画面のレイアウトを改善

iPad からも投稿しやすいように投稿画面のレイアウトも修正した。本文のテキストエリアが広がったためスクロールしないと「スラッグ」以降の入力欄にアクセスできなくなったので、ある程度横幅のあるウィンドウのときにはこれらを右側に配置するようにした。

加えて、フォームを書きかけで保存したかどうかがはっきりせず、未保存の内容があるのに保存せずページから離脱してしまうことがあったので、変更点があるときは背景色を変えてわかるようにした。これにより記入内容を保存せずページを離脱してしまい、内容が失われるという悲劇を回避できるようになった。やり方は適当に検索して出てきた Stack Overflow を参考に、ページを読み込んだ時点で JavaScript でフィールド内の要素を JSON.stringify して data attribute に格納し、その後各フィールドの input イベントを監視して変更があるかどうかをチェックしている。

class FormObserver {
  constructor() {
    this.initializeFields();
    this.observeFieldsChange();
  }

  initializeFields() {
    const fields = Array.from(document.querySelectorAll('div.field'));
    for (const field of fields) {
      const inputElement = field.querySelector('input[type="text"], textarea, input[type="datetime-local"], select option:checked');
      if (inputElement === null) {
        continue;
      }
      field.dataset.serialize = JSON.stringify(inputElement.value);
    }
  }

  observeFieldsChange() {
    const fields = Array.from(document.querySelectorAll('div.field'));
    for (const field of fields) {
      const inputElement = field.querySelector('input[type="text"], textarea, input[type="datetime-local"], select');
      if (inputElement === null) {
        continue;
      }
      inputElement.addEventListener('input', () => {
        if (field.dataset.serialize != JSON.stringify(inputElement.value)) {
          inputElement.dataset.changed = 'true';
          field.classList.add('edited');
        } else {
          inputElement.dataset.changed = 'false';
          field.classList.remove('edited');
        }
      })
    }
  }
}

input イベントを監視すると動作がもっさりするのではないかと心配したが、そんなことはないようだ。普通に使えている。

いまのところ管理画面の HTML レンダリングはサーバーサイドで Ruby で行っているが、これ以上凝ったことをやるなら React などを使って JavaScript で HTML を組み立てる方式にしていく方が効率が良さそうだ。

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

7 年間眠っていたブランチを起こして、 Lokka の ActiveRecord 化に取り組み始めた。元のブランチは hrysd さんが取り組んでいたやつだ。

現在の master の内容を取り込むのが大変だった。 active-record ブランチでは ActiveRecord 化と同時に様々な改良・改変が行われていて、 master の内容と思い切りコンフリクトするものがあったりして、コンフリクトの解消作業はかなり大変だった。

active-record の大きな変更点は以下。

  1. カスタムパーマリンク機能の削除
  2. 「もっと読む」機能の削除
  3. カテゴリーをネストさせる機能の削除
  4. ユーザー認証方法の変更(カラムの追加)

このうち 1 と 2 は削除された機能を復活させた。自分が使っていてなくなると困るし、特にカスタムパーマリンクは既存サイトでこの機能を使っているところがデッドリンクだらけになって散々な目に遭ってしまう。 4 に関しても、 master の認証方法と互換性を持たせないと既存ユーザーがログインできなくなるので古い認証方法でもログインできるようにした。

3 に関しては WordPress との互換性を考えると必要かもしれないが、自分で使ってなくてユースケースが思い浮かばないのでいらないかなという感じがする。そもそも Lokka は WordPress キラーとなるべく Fjord 社内で作られ始めたと認識しているが、 WordPress は相変わらず元気だし Lokka の利用状況的にも WordPress alternative を目指す必要はないと思う。

そのほか、 rake db:delete が動かなかったのを直したり bundle update をしてぶっ壊れたところを直したり、デフォルト以外のテーマが ActiveRecord 化してなかったのを対応させたり( dm-pagination から kaminari へ移行)して ActiveRecord 5 で概ね動くところまで持ってくることができた。

ActiveRecord は良くできていて、 DataMapper だと難しかった JOIN した上での集計クエリなどが書きやすい。ドキュメントが山ほどあるのもよい。 DataMapper は情報が少ないのが一番つらかった。一方で DataMapper だと気にする必要がなかった N+1 問題を自分で解決する必要がある。 View でうかつに参照するテーブルのデータを増やすと N+1 問題が発生して途端にパフォーマンスが劣化する。

また、誰がどんな DB で利用するかわからない状況で db/schema.rb を git で追跡してよいものかというのもひかっかる。 ActiveRecord を使う以上、 migration と schema.rb からは逃げられないのだが、 MySQL で使う人も PostgreSQL で使う人も SQLite で使う人もいて、それぞれの DB でマイグレーションを実行するごとに異なる schema.rb が吐き出されるので git で追跡すべきではないのではないかと思う。どんなデータベースで利用されるかを意識せずに開発できる、という点では DataMapper の方が CMS 開発向きだったと思う。

以前の Lokka であればあまり Ruby 知らない人でもとりあえず git clone して自分の好みのテーマを追加して Heroku に push すれば動かせたが、 ActiveRecord 化することで N+1 問題など Rails に強くないと触りにくい感じになってしまった。ただ、 Sass は Ruby を捨てて C に移行したし、 Slim なんかも JavaScript フロントエンド技術の盛り上がりの陰で開発は停滞している。こういう時勢になってくるとフロントエンドに強いマークアップエンジニア兼ウェブデザイナー的な人が Ruby 製の CMS を使う動機はなくなってしまう。 CMS を使ったサイト構築でも Sass や Slim を使って HTML コーディングの生産性を上げ、 Heroku を使って簡単に deploy できる、というのが komagata さん達が最初に想定してた Lokka のユースケースだと思うけど、 JavaScript によるフロントエンド技術が強力になりすぎて、生産性の高いフロントサイド開発のために Ruby を経由する必要がなくなってしまった。


これから Lokka はどうあるべきなのだろうか。モダンなフロントエンドフレームワークは強力だ。否が応でも JAMStack に対応していくしかないだろうと思う。つまり Sinatra で作るのは API (と管理画面)だけになり、フロントエンドは React や Vue.js で作るべきだろう。ちょっとしたサイトを JAMStack で構築したいが、 API に良いのがない、とはいえ Rails は使いたくない、というケースで Lokka を使うという感じだろうか。ただ、いまは Firebase なんかもあるのでそもそも API を自前で持つ必要はないのかもしれない。どのみちかなりニッチなユースケースになるだろう。

ちなみにこのブログの Archive ページは中途半端ながら React で作っていて割といい感じに動いている。 ActiveRecord 化が済んだら React でサイト全体を作り直してみたい。

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

DataMapper のドキュメントを見たくてググったが出てくるのは Stack Overflow ばかりで公式サイトが検索結果に出てこない。 GitHub の DataMapper のリポジトリ( Archive されている)経由で見に行ってみると、なんと ROM ( Ruby Object Mapper ) のページにリダイレクトされた。

ROM は Hanami で使われる ORM で、 DataMapper よりもさらに ActiveRecord と使い心地が異なる。

Qiita の以下の記事を読むと使い方のイメージが湧く。

軽くてシンプルなのだろうがだいぶ特殊だ。

Lokka の使い手は少なくとも Heroku が使える人で、そういう人ならば ActiveRecord の方が Rails の本やドキュメントで学びやすいはずだ。というわけで早めに、真剣に ActiveRecord への移行を考えなければならない。

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

2019-08-24 06.57.38.gif

Lokka のプレビューはサーバーサイドに保存時と同じパラメーターを POST して、 DB にレコードを保存せずレンダリングだけ行うが、 GitHub や Kibela のようなタブ切り替えでインスタントに Markdown のプレビューができると便利だろうと思って、 Markdown で編集中に Edit と Preview をタブっぽい UI で切り替えられるようにしてみた。

Markdown のレンダリング自体はサーバーサイドで行っているので、プレビューしたときと実際に保存したときで Markdown の解釈が異なるということもない。

ただリモートで生成した HTML 文字列を DOM に挿入するときに単純に document.innerHTML = body; のようなやり方をしてしまうと JavaScript が動かず困ったので、 iframe を作って document.open(); document.write(body); document.close(); するやり方をとった。そうなると今度は iframe の高さを body のレンダリング終了後に調整する必要があって色々面倒だった。たまに高さがずれたりはするが、基本的にはめっちゃ便利。

Lokka の利用者少ないと思いますが、 Lokka 本体にも入れようと思います。 Ruby の最新版への追従へも最近は行えてないのでこれもやります。

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

このブログの記事投稿画面で、画像をコピー・アンド・ペーストでアップロードできるようにした。以前、ドラッグ・アンド・ドロップではアップロードできるようにしていたが、 GitHub や Kibela ではコピー・アンド・ペーストでアップロードできるようになっておりはちゃめちゃに便利だったので真似してみた。こんな感じ。

2019-08-18 15.31.56.gif

ハイパー便利。

Reference

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

Lokka の wysiwyg エディターは jQuery ベースの jwysiwyg といつやつが採用されている。

しかしここ最近はメンテナンスされてなくて、最近の Chrome では利用できない状況だったりする。以前、ファイルアップロード機能を付けたときも jwysiwyg の不具合が原因で wysiwyg モードでの画像アップロードは断念した。

最近のモダンな wysiwyg はどんなのがあるんだろうと調べてみたら Quill ってのが GitHub で 18000 くらいスター付いてて良さそうだった。

ちょっと試してみたところ wysiwyg で画像のアップロードもできるようになったので jwysiwyg からこいつに置き換えるのも悪くはなさそう。

ただ Quill は擬似 textarea なので実際の form には対応していない。 SPA で利用されることが想定されており、 Ajax 前提なつくりとなっている。サーバーサイドで HTML をレンダリングする昔ながらのウェブアプリで使うには一手間必要そうだった( form の onsubmit で texarea を createElement して Quill で入力したデータをぶっこむなど)。

追記

Pull Request 出した。

さらに追記

↑の Pull Request 、 Merge されたんで Lokka の wysiwyg エディターは Quill となりました。

| @ブログ

Facebook にウェブサイトの URL をはっつけるとき参照される HTML メタ情報の仕組みに Open Graph Protocol ってのがある。 Facebook に URL を貼ると bot が URL の内容を読みに行ってページの概要や画像を取得し Facebook 内に埋め込み表示するというもの。 Facebook を見ている人はリンク先の内容をクリックする前に概要を把握できるので、リンクをクリックして見たい情報じゃなかった、ということを避けられる。 Facebook が考案して策定した仕組みだけど、 Facebook に限らずいろんなサイトで OGP タグを出力してるし読み込んでる。 Twitter にも似た仕組みあって Twitter Card という。この辺の対応は結構前にやってた。

ただ自分のサイトが OGP タグを提供するだけではつまんないなと思ったので自分のブログにペロッと URL を貼ったときに相手先に OGP タグがあればそれを出力するようにしてみた。こんな感じ。

OGP Preview

しかしここで困ったことがあって、↑でリンクしてる Circle のサイトは HTTPS で配信されておらず、単純に Circle のサイトで og:image に指定されている画像を SSL 化されているこのブログで読み込むと Mixed Content になってしまう。せっかく HTTP/2 で配信していたのに台なしになってしまう。またそもそも og:image は Facebook でシャアされることを想定されていることがほとんどなので、画像サイズがデカすぎていい感じにスクエアに表示するためには CSS の小技を駆使したりする必要があった。

いい感じに解決する方法ないかなと調べていたら良いのが見つかった。

Go で書かれた Image Proxy Server で、 HTTPS Proxy は当然のこと動的リサイズもできる。使い方は簡単でバイナリを落としてきて動かすだけ。 Go なんで ImageMagick をどうしたりとかを考えなくて良い。 そもそも Docker イメージも提供されているので Docker をインストール済みなら docker run するだけでも動く。 めっちゃお手軽。

こいつのおかげで HTTPS で配信されていないサイトの OGP タグを読み込んでも Mixed Content にならずに済むようになった。また og:image は適切にリサイズできるようになった。画像変換サーバーとかは結構難しいやつで個人のブログでこんなに簡単に使えるものだとは思ってなかったので正直ビックリした。

AWS の登場で大企業じゃなくても CDN 使ったり仮想サーバーでウェブシステムを構築したりできるようになった。さらには Go や Docker といった技術のおかげで複数の込み入ったソフトウェアを組み合わせて構築していく必要があったシステムが、まるで jQuery を使うような感覚でポン付けで使える時代になってきている。とても素晴らしい。

ちなみに OGP の取得には open_graph_reader という gem を使っている(昔からある opengraph という gem はメンテナンスされておらず最近の Nokogiri で動かない)。 open_graph_reader の作者が結構 Opinionated な人で、以下のような Anti-featurs を掲げている。

open_graph_reader Anti-features

http://ogp.me/ の仕様に準拠していないサイトのことは完全無視というつくり。個人的にはこういう思想は好みだが、現実問題として使い勝手が悪い。例えば hitode909 さんのブログの OGP タグを取得しようとしたところ以下のようなエラーを出して取ってくれなかった。

スクリーンショット 2018-05-26 10.08.47.png

article:published_time は ISO8601 形式の datetime であるべき、とのこと。はてなブログはかなりシェアが大きくリンクする機会が多いので残念。