Skip to content

Instantly share code, notes, and snippets.

@fracai
Created May 22, 2017 01:47
Show Gist options
  • Save fracai/30b1fea1740962c3a5ebb0396c08e3fc to your computer and use it in GitHub Desktop.
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/
# 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