Created
May 22, 2017 01:47
-
-
Save fracai/30b1fea1740962c3a5ebb0396c08e3fc to your computer and use it in GitHub Desktop.
A nanoc blogging helper (without AtomFeedBuilder inheritance) for producing a JSON feed. See https://jsonfeed.org/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
module Nanoc::Helpers | |
# @see http://nanoc.ws/doc/reference/helpers/#blogging | |
module Blogging_JSON | |
# @return [Array] | |
def articles | |
blk = -> { @items.select { |item| item[:kind] == 'article' } } | |
if @items.frozen? | |
@article_items ||= blk.call | |
else | |
blk.call | |
end | |
end | |
# @return [Array] | |
def sorted_articles | |
blk = -> { articles.sort_by { |a| attribute_to_time(a[:created_at]) }.reverse } | |
if @items.frozen? | |
@sorted_article_items ||= blk.call | |
else | |
blk.call | |
end | |
end | |
class JSONFeedBuilder | |
include Nanoc::Helpers::Blogging | |
attr_accessor :config | |
attr_accessor :limit | |
attr_accessor :relevant_articles | |
attr_accessor :preserve_order | |
attr_accessor :content_proc | |
attr_accessor :excerpt_proc | |
attr_accessor :title | |
attr_accessor :author_name | |
attr_accessor :author_uri | |
attr_accessor :icon | |
attr_accessor :logo | |
def initialize(config, item) | |
@config = config | |
@item = item | |
end | |
def validate | |
validate_config | |
validate_feed_item | |
validate_articles | |
end | |
def build | |
require 'json' | |
JSON[build_for_feed()] | |
end | |
protected | |
def sorted_relevant_articles | |
all = relevant_articles | |
unless @preserve_order | |
all = all.sort_by { |a| attribute_to_time(a[:created_at]) } | |
end | |
all.reverse.first(limit) | |
end | |
def last_article | |
sorted_relevant_articles.first | |
end | |
def updated | |
relevant_articles.map { |a| attribute_to_time(a[:updated_at] || a[:created_at]) }.max | |
end | |
def validate_config | |
if @config[:base_url].nil? | |
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build JSON feed: site configuration has no base_url') | |
end | |
end | |
def validate_feed_item | |
if title.nil? | |
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build JSON feed: no title in params, item or site config') | |
end | |
if author_name.nil? | |
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build JSON feed: no author_name in params, item or site config') | |
end | |
if author_uri.nil? | |
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build JSON feed: no author_uri in params, item or site config') | |
end | |
end | |
def validate_articles | |
if relevant_articles.empty? | |
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build JSON feed: no articles') | |
end | |
if relevant_articles.any? { |a| a[:created_at].nil? } | |
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build JSON feed: one or more articles lack created_at') | |
end | |
end | |
def build_for_feed() | |
root_url = @config[:base_url] + '/' | |
json = {} | |
json['version'] = 'https://jsonfeed.org/version/1' | |
# Add primary attributes | |
json['home_page_url'] = root_url | |
json['title'] = title | |
## Add date | |
#xml.updated(updated.__nanoc_to_iso8601_time) | |
# Add links | |
json['feed_url'] = feed_url | |
# Add author information | |
json['author'] = {} | |
json['author']['name'] = author_name | |
json['author']['url'] = author_uri | |
# Add icon and logo | |
json['icon'] = icon if icon | |
json['favicon'] = logo if logo | |
# Add articles | |
json['items'] = [] | |
sorted_relevant_articles.each do |a| | |
json['items'] << build_for_article(a) | |
end | |
json | |
end | |
def build_for_article(a) | |
# Get URL | |
url = url_for(a) | |
return if url.nil? | |
json = {} | |
# Add primary attributes | |
json['id'] = json_tag_for(a) | |
json['title'] = a[:title] | |
# Add dates | |
json['date_published'] = attribute_to_time(a[:created_at]).__nanoc_to_iso8601_time | |
json['date_modified'] = attribute_to_time(a[:updated_at] || a[:created_at]).__nanoc_to_iso8601_time | |
# Add specific author information | |
if a[:author_name] || a[:author_uri] | |
json['author']['name'] = a[:author_name] || author_name | |
json['url'] = a[:author_uri] || author_uri | |
end | |
# Add link | |
json['url'] = url | |
# Add content | |
summary = excerpt_proc.call(a) | |
json['content_html'] = content_proc.call(a) | |
json | |
end | |
end | |
# @option params [Number] :limit | |
# @option params [Array] :articles | |
# @option params [Boolean] :preserve_order | |
# @option params [Proc] :content_proc | |
# @option params [Proc] :excerpt_proc | |
# @option params [String] :title | |
# @option params [String] :author_name | |
# @option params [String] :author_uri | |
# @option params [String] :icon | |
# @option params [String] :logo | |
# | |
# @return [String] | |
def json_feed(params = {}) | |
require 'builder' | |
# Create builder | |
builder = JSONFeedBuilder.new(@config, @item) | |
# Fill builder | |
builder.limit = params[:limit] || 5 | |
builder.relevant_articles = params[:articles] || articles || [] | |
builder.preserve_order = params.fetch(:preserve_order, false) | |
builder.content_proc = params[:content_proc] || ->(a) { a.compiled_content(snapshot: :pre) } | |
builder.excerpt_proc = params[:excerpt_proc] || ->(a) { a[:excerpt] } | |
builder.title = params[:title] || @item[:title] || @config[:title] | |
builder.author_name = params[:author_name] || @item[:author_name] || @config[:author_name] | |
builder.author_uri = params[:author_uri] || @item[:author_uri] || @config[:author_uri] | |
builder.icon = params[:icon] | |
builder.logo = params[:logo] | |
# Run | |
builder.validate | |
builder.build | |
end | |
# @return [String] | |
def url_for(item) | |
# Check attributes | |
if @config[:base_url].nil? | |
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build JSON feed: site configuration has no base_url') | |
end | |
# Build URL | |
if item[:custom_url_in_feed] | |
item[:custom_url_in_feed] | |
elsif item[:custom_path_in_feed] | |
@config[:base_url] + item[:custom_path_in_feed] | |
elsif item.path | |
@config[:base_url] + item.path | |
end | |
end | |
# @return [String] | |
def feed_url | |
# Check attributes | |
if @config[:base_url].nil? | |
raise Nanoc::Int::Errors::GenericTrivial.new('Cannot build JSON feed: site configuration has no base_url') | |
end | |
@item[:feed_url] || @config[:base_url] + @item.path | |
end | |
# @return [String] | |
def json_tag_for(item) | |
hostname, base_dir = %r{^.+?://([^/]+)(.*)$}.match(@config[:base_url])[1..2] | |
formatted_date = attribute_to_time(item[:created_at]).__nanoc_to_iso8601_date | |
'tag:' + hostname + ',' + formatted_date + ':' + base_dir + (item.path || item.identifier.to_s) | |
end | |
# @param [String, Time, Date, DateTime] arg | |
# | |
# @return [Time] | |
def attribute_to_time(arg) | |
case arg | |
when DateTime | |
arg.to_time | |
when Date | |
Time.utc(arg.year, arg.month, arg.day) | |
when String | |
Time.parse(arg) | |
else | |
arg | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment