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. 「プレビュー」をどのレベルまで求めるのか?といった問題はあるが。 

More

Categories

Tool 日々 Web Biz Net Apple MS ことば News Unix howto Food PHP Movie Edu Community Book Security Text TV Perl Ruby Music Pdoc 生き方 RDoc ViewCVS CVS Rsync Disk Mail FreeBSD Cygwin PDF Photo Zebedee Debian OSX Comic Cron Sysadmin Font Analog iCal Sunbird DNS Linux Wiki Emacs Thunderbird Sitecopy Terminal Drawing tDiary AppleScript Life Money Omni PukiWiki Xen XREA Zsh Screen CASL Firefox Fink zsh haXe Ecmascript PATH_INFO SQLite PEAR Lighttpd FastCGI Subversion au prototype.js jsUnit Apache Trac Template Java Rhino Mochikit Feed Bloglines CSS del.icio.us SBS qwikWeb gettext Ajax JSDoc Rails HTML CHM EPWING NDTP EB IE CLI ck ThinkPad Toy WSH RFC readline rlwrap ImageMagick epeg Frenzy sysprep Ubuntu MeCab DTP ERD DBMS eclipse Eclipse Awk RD Diigo XAMPP RubyGems PHPDoc iCab DOM YAML Camino Geekmonkey w3m Scheme Gauche Lisp JSAN Google VMware DSL SLAX Safari Markdown Textile IRC Jabber Fastladder MacPorts LLSpirit CPAN Mozilla Twitter OpenFL Rswatch ITS NTP GUI Pragger Yapra XML Mobile Git Study JSON VirtualBox Samba Pear Growl Mercurial Rack Capistrano Rake Win RSS Mechanize Sitemaps Android JavaScript Python RTM OOo iPod Yahoo Unicode Github iTunes God SBM friendfeed Friendfeed HokuUn Sinatra TDD Test Project Evernote iPad Geohash Location Map Search Simplenote Image WebKit RSpec Phone CSV WiMAX USB Chrome RubyKaigi RubyKaigi2011 Space CoffeeScript Nokogiri Hpricot Rubygems jQuery Node GTD CI UX Design VCS Kanazawa.rb Kindle Amazon Agile Vagrant Chef Windows Composer Dotenv PaaS Itamae SaaS Docker Swagger Grape WebAPI Microservices OmniAuth HTTP 分析基盤 CDN Terraform IaaS HCL Webpack Vue.js BigQuery Middleman CMS AWS PNG Laravel Selenium OAuth OpenAPI GitHub UML GCP TypeScript SQL Hanami Develop Document Jekyll