2018-12-09

Jekyllの外の世界からdataを作る準備

※ 実際には外の世界へのアクセスの部分は何も扱ってません。あくまで Reader が仕事する前に data を作ることができた、ってだけ。また Assetを外に置く話 とも別の話。

My first Jekyll GeneratorのためにJekyllを読んでみて分かったんだけど、Jekyll::Hooks を利用すると Jekyll の外からデータを持ってきたあとに reader が仕事するようにすることは割と簡単にできそうだ。

Jekyll 自体はあくまで local の file を相手にする静的サイトジェネレータだが、これを活かして plugin 的なもので外部からデータを持ってきて file を作ってしまえば そのデータはどこからやってくるものであろうと構わない、つまりデータの生成、置き場所は自由ということになる。

ということは Jekyll 以外の何かでコンテンツを作れる、言い換えると Decoupled CMS のできあがりだ。1

とりあえず毎回のprocessのたびに何かする

Jekyll::Site#process をもう一度読むと

   def process
     reset
     read
     generate
     render
     cleanup
     write
     print_stats if config["profile"]
   end

のように毎回 reset が走っているので、適当な plugin を作って2、その最後に

Jekyll::Hooks.register :site, :after_reset do |site|
  MyFirstResetPlugin.new(site)
end

みたいなコードを書いておくと、自動でサイトの内容が空っぽの状態で Plugin が実行されるので、そこで site.config['data_dir'] の中に対してなんらかのデータを吐いてあげれば、Generator からは自動的に site.data として取得できるようになる。

例えばこんな感じ。

class ExternalData < Jekyll::Plugin
  def initialize(site)
    @dir  = File.join(site.config['source'], site.config['data_dir'])
    @file = File.join(@dir, 'external.yml')
  end
  attr_reader :dir, :file

  def fetch_from_api
    FileUtils.mkdir_p(dir)
    open(file, 'w') {|f|
      f.puts YAML.dump(foo: 'bar')
    }
  end
end

Jekyll::Hooks.register :site, :after_reset do |site|
  ExternalData.new(site).fetch_from_api
end

とすると、build のたびに API からデータを取得するような仕組みが可能だ。

※ ここでは API から取得する処理は何も書いてないので data を吐いているだけ。

after_resetなのかafter_initなのかそれとも

ただ上のやり方だとちょっと現実的じゃない。

少なくとも実際に本物の API へアクセスするような仕組みを毎回の process のたびに走らせるのは開発環境では高コストすぎる。だからたぶんこんな感じにして、

Jekyll::Hooks.register :site, :after_init do |site|
  if ENV['FORCE_FETCH']
    ExternalData.new(site).fetch_from_api
  end
end

みたいに環境変数で切り替えて、必要な時にだけ API アクセスするような仕組みがあるとよさそう。3

これを応用して data でも collection でもなんでも作って、その後に Generator に渡してや れば Decoupled CMS への第一歩を踏み出せたと言ってよさそう。

残りはCMSとビルドサーバの連携(例えばWebhook)

CMS を Jekyll や静的サイトジェネレータ側で担わないようになると、

  1. コンテンツの更新
  2. コンテンツの更新から kick されたサイトのビルド
  3. ビルドしたサイトの deploy

の 1 と 2 をどうやって繋ぐのかという問題になる。

例えば CMS が WordPress だったら WordPress の更新を Webhook でビルドサーバに送り、ビルドサーバ上で WP REST API からコンテンツを取得してビルド、といった具合。

これが可能になれば CMS としては WordPress を活かしたまま、表側はエンジニア、デザイナが余計なこと4を気にせずに、かつ存分にモダンな作りで改善するために WordPress から分離するといったことが可能になる。5

感想

今後静的サイトジェネレータを評価する機会があったら、簡単に始めることができることも速さも大事だけど、Asset も Data も分離しやすい、という視点での評価を少なくとも個人的には大事にしいきたいなぁと思いましたとさ。

  1. 実際には site オブジェクトにさえつっこめればいいので、site に ActiveRecord をぶらさげる、みたいなやり方でもいいけど、今回は扱わない。 

  2. plugin でなくてもいいけど、plugins_dir 以下は自動で require されるし、乗っておいた方が混乱がなくてよい。 

  3. 本気でコンテンツの量がすごいことになったらCI的な意味ではなくgenerate用のビルドサーバを用意してそこにストレージも用意して差分でfetchするようなものが必要かもしれないが、その辺は今回の範疇外。 

  4. セキュリティの問題はあるけどバージョンアップでプラグインが動かなくなってしまったらどうしよう?とか 

  5. 「プレビュー」をどのレベルまで求めるのか?といった問題はあるが。 

About

例によって個人のなんちゃらです