rams-jekyll-feed 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +2 -0
- data/.rubocop.yml +27 -0
- data/.travis.yml +32 -0
- data/Gemfile +11 -0
- data/History.markdown +165 -0
- data/LICENSE.txt +22 -0
- data/README.md +206 -0
- data/Rakefile +8 -0
- data/appveyor.yml +29 -0
- data/lib/jekyll-feed.rb +12 -0
- data/lib/jekyll-feed/feed.xml +96 -0
- data/lib/jekyll-feed/generator.rb +112 -0
- data/lib/jekyll-feed/meta-tag.rb +37 -0
- data/lib/jekyll-feed/page-without-a-file.rb +9 -0
- data/lib/jekyll-feed/version.rb +7 -0
- data/rams-jekyll-feed.gemspec +30 -0
- data/script/bootstrap +3 -0
- data/script/cibuild +7 -0
- data/script/fmt +10 -0
- data/script/release +7 -0
- data/script/test +4 -0
- data/spec/fixtures/_collection/2018-01-01-collection-doc.md +4 -0
- data/spec/fixtures/_collection/2018-01-02-collection-category-doc.md +5 -0
- data/spec/fixtures/_config.yml +9 -0
- data/spec/fixtures/_data/authors.yml +5 -0
- data/spec/fixtures/_drafts/2015-01-12-a-draft.md +4 -0
- data/spec/fixtures/_layouts/some_default.html +11 -0
- data/spec/fixtures/_posts/2013-12-12-dec-the-second.md +7 -0
- data/spec/fixtures/_posts/2014-03-02-march-the-second.md +6 -0
- data/spec/fixtures/_posts/2014-03-04-march-the-fourth.md +9 -0
- data/spec/fixtures/_posts/2015-01-18-jekyll-last-modified-at.md +5 -0
- data/spec/fixtures/_posts/2015-02-12-strip-newlines.md +6 -0
- data/spec/fixtures/_posts/2015-05-12-liquid.md +7 -0
- data/spec/fixtures/_posts/2015-05-12-pre.html +8 -0
- data/spec/fixtures/_posts/2015-05-18-author-detail.md +9 -0
- data/spec/fixtures/_posts/2015-08-08-stuck-in-the-middle.html +6 -0
- data/spec/fixtures/_posts/2016-04-25-author-reference.md +6 -0
- data/spec/fixtures/feed.xslt.xml +0 -0
- data/spec/jekyll-feed_spec.rb +524 -0
- data/spec/spec_helper.rb +30 -0
- metadata +214 -0
data/Rakefile
ADDED
data/appveyor.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
version: "{build}"
|
2
|
+
clone_depth: 5
|
3
|
+
build: off
|
4
|
+
|
5
|
+
environment:
|
6
|
+
NOKOGIRI_USE_SYSTEM_LIBRARIES: true
|
7
|
+
JEKYLL_VERSION: "~> 3.8"
|
8
|
+
matrix:
|
9
|
+
- RUBY_FOLDER_VER: "26"
|
10
|
+
JEKYLL_VERSION : "~> 3.7.4"
|
11
|
+
- RUBY_FOLDER_VER: "26"
|
12
|
+
JEKYLL_VERSION : ">= 4.0.0.pre.alpha1"
|
13
|
+
- RUBY_FOLDER_VER: "26"
|
14
|
+
- RUBY_FOLDER_VER: "24"
|
15
|
+
- RUBY_FOLDER_VER: "23"
|
16
|
+
|
17
|
+
install:
|
18
|
+
- SET PATH=C:\Ruby%RUBY_FOLDER_VER%-x64\bin;%PATH%
|
19
|
+
- bundle install --retry 5 --jobs=%NUMBER_OF_PROCESSORS% --clean --path vendor\bundle
|
20
|
+
|
21
|
+
test_script:
|
22
|
+
- ruby --version
|
23
|
+
- gem --version
|
24
|
+
- bundler --version
|
25
|
+
- bash ./script/test
|
26
|
+
|
27
|
+
cache:
|
28
|
+
# If one of the files after the right arrow changes, cache will be invalidated
|
29
|
+
- 'vendor\bundle -> appveyor.yml, Gemfile, jekyll-feed.gemspec'
|
data/lib/jekyll-feed.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll"
|
4
|
+
require "fileutils"
|
5
|
+
require "jekyll-feed/generator"
|
6
|
+
|
7
|
+
module JekyllFeed
|
8
|
+
autoload :MetaTag, "jekyll-feed/meta-tag"
|
9
|
+
autoload :PageWithoutAFile, "jekyll-feed/page-without-a-file.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
Liquid::Template.register_tag "feed_meta", JekyllFeed::MetaTag
|
@@ -0,0 +1,96 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
{% if page.xsl %}
|
3
|
+
<?xml-stylesheet type="text/xml" href="{{ '/feed.xslt.xml' | absolute_url }}"?>
|
4
|
+
{% endif %}
|
5
|
+
<feed xmlns="http://www.w3.org/2005/Atom" {% if site.lang %}xml:lang="{{ site.lang }}"{% endif %}>
|
6
|
+
<generator uri="https://jekyllrb.com/" version="{{ jekyll.version }}">Jekyll</generator>
|
7
|
+
<link href="{{ page.url | absolute_url }}" rel="self" type="application/atom+xml" />
|
8
|
+
<link href="{{ '/' | absolute_url }}" rel="alternate" type="text/html" {% if site.lang %}hreflang="{{ site.lang }}" {% endif %}/>
|
9
|
+
<updated>{{ site.time | date_to_xmlschema }}</updated>
|
10
|
+
<id>{{ page.url | absolute_url | xml_escape }}</id>
|
11
|
+
|
12
|
+
{% assign title = site.title | default: site.name %}
|
13
|
+
{% if page.collection != "posts" %}
|
14
|
+
{% assign collection = page.collection | capitalize %}
|
15
|
+
{% assign title = title | append: " | " | append: collection %}
|
16
|
+
{% endif %}
|
17
|
+
{% if page.category %}
|
18
|
+
{% assign category = page.category | capitalize %}
|
19
|
+
{% assign title = title | append: " | " | append: category %}
|
20
|
+
{% endif %}
|
21
|
+
|
22
|
+
{% if title %}
|
23
|
+
<title type="html">{{ title | smartify | xml_escape }}</title>
|
24
|
+
{% endif %}
|
25
|
+
|
26
|
+
{% if site.description %}
|
27
|
+
<subtitle>{{ site.description | xml_escape }}</subtitle>
|
28
|
+
{% endif %}
|
29
|
+
|
30
|
+
{% if site.author %}
|
31
|
+
<author>
|
32
|
+
<name>{{ site.author.name | default: site.author | xml_escape }}</name>
|
33
|
+
{% if site.author.email %}
|
34
|
+
<email>{{ site.author.email | xml_escape }}</email>
|
35
|
+
{% endif %}
|
36
|
+
{% if site.author.uri %}
|
37
|
+
<uri>{{ site.author.uri | xml_escape }}</uri>
|
38
|
+
{% endif %}
|
39
|
+
</author>
|
40
|
+
{% endif %}
|
41
|
+
|
42
|
+
{% assign posts = site[page.collection] | where_exp: "post", "post.draft != true" | sort: "date" | reverse %}
|
43
|
+
{% if page.category %}
|
44
|
+
{% assign posts = posts | where: "category",page.category %}
|
45
|
+
{% endif %}
|
46
|
+
{% for post in posts %}
|
47
|
+
<entry{% if post.lang %}{{" "}}xml:lang="{{ post.lang }}"{% endif %}>
|
48
|
+
<title type="html">{{ post.title | smartify | strip_html | normalize_whitespace | xml_escape }}</title>
|
49
|
+
<link href="{{ post.url | absolute_url }}" rel="alternate" type="text/html" title="{{ post.title | xml_escape }}" />
|
50
|
+
<published>{{ post.date | date_to_xmlschema }}</published>
|
51
|
+
<updated>{{ post.last_modified_at | default: post.date | date_to_xmlschema }}</updated>
|
52
|
+
<id>{{ post.id | absolute_url | xml_escape }}</id>
|
53
|
+
{% assign excerpt_only = post.feed.excerpt_only | default: site.feed.excerpt_only %}
|
54
|
+
{% unless excerpt_only %}
|
55
|
+
<content type="html" xml:base="{{ post.url | absolute_url | xml_escape }}">{{ post.content | strip | xml_escape }}</content>
|
56
|
+
{% endunless %}
|
57
|
+
|
58
|
+
{% assign post_author = post.author | default: post.authors[0] | default: site.author %}
|
59
|
+
{% assign post_author = site.data.authors[post_author] | default: post_author %}
|
60
|
+
{% assign post_author_email = post_author.email | default: nil %}
|
61
|
+
{% assign post_author_uri = post_author.uri | default: nil %}
|
62
|
+
{% assign post_author_name = post_author.name | default: post_author %}
|
63
|
+
|
64
|
+
<author>
|
65
|
+
<name>{{ post_author_name | default: "" | xml_escape }}</name>
|
66
|
+
{% if post_author_email %}
|
67
|
+
<email>{{ post_author_email | xml_escape }}</email>
|
68
|
+
{% endif %}
|
69
|
+
{% if post_author_uri %}
|
70
|
+
<uri>{{ post_author_uri | xml_escape }}</uri>
|
71
|
+
{% endif %}
|
72
|
+
</author>
|
73
|
+
|
74
|
+
{% if post.category %}
|
75
|
+
<category term="{{ post.category | xml_escape }}" />
|
76
|
+
{% endif %}
|
77
|
+
|
78
|
+
{% for tag in post.tags %}
|
79
|
+
<category term="{{ tag | xml_escape }}" />
|
80
|
+
{% endfor %}
|
81
|
+
|
82
|
+
{% if post.excerpt and post.excerpt != empty %}
|
83
|
+
<summary type="html">{{ post.excerpt | strip_html | normalize_whitespace | xml_escape }}</summary>
|
84
|
+
{% endif %}
|
85
|
+
|
86
|
+
{% assign post_image = post.image.path | default: post.image %}
|
87
|
+
{% if post_image %}
|
88
|
+
{% unless post_image contains "://" %}
|
89
|
+
{% assign post_image = post_image | absolute_url %}
|
90
|
+
{% endunless %}
|
91
|
+
<media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="{{ post_image | xml_escape }}" />
|
92
|
+
<media:content medium="image" url="{{ post_image | xml_escape }}" xmlns:media="http://search.yahoo.com/mrss/" />
|
93
|
+
{% endif %}
|
94
|
+
</entry>
|
95
|
+
{% endfor %}
|
96
|
+
</feed>
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JekyllFeed
|
4
|
+
class Generator < Jekyll::Generator
|
5
|
+
safe true
|
6
|
+
priority :lowest
|
7
|
+
|
8
|
+
# Main plugin action, called by Jekyll-core
|
9
|
+
def generate(site)
|
10
|
+
@site = site
|
11
|
+
collections.each do |name, meta|
|
12
|
+
Jekyll.logger.info "Jekyll Feed:", "Generating feed for #{name}"
|
13
|
+
(meta["categories"] + [nil]).each do |category|
|
14
|
+
path = feed_path(:collection => name, :category => category)
|
15
|
+
next if file_exists?(path)
|
16
|
+
|
17
|
+
@site.pages << make_page(path, :collection => name, :category => category)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Matches all whitespace that follows
|
25
|
+
# 1. A '>', which closes an XML tag or
|
26
|
+
# 2. A '}', which closes a Liquid tag
|
27
|
+
# We will strip all of this whitespace to minify the template
|
28
|
+
MINIFY_REGEX = %r!(?<=>|})\s+!.freeze
|
29
|
+
|
30
|
+
# Returns the plugin's config or an empty hash if not set
|
31
|
+
def config
|
32
|
+
@config ||= @site.config["feed"] || {}
|
33
|
+
end
|
34
|
+
|
35
|
+
# Determines the destination path of a given feed
|
36
|
+
#
|
37
|
+
# collection - the name of a collection, e.g., "posts"
|
38
|
+
# category - a category within that collection, e.g., "news"
|
39
|
+
#
|
40
|
+
# Will return "/feed.xml", or the config-specified default feed for posts
|
41
|
+
# Will return `/feed/category.xml` for post categories
|
42
|
+
# WIll return `/feed/collection.xml` for other collections
|
43
|
+
# Will return `/feed/collection/category.xml` for other collection categories
|
44
|
+
def feed_path(collection: "posts", category: nil)
|
45
|
+
prefix = collection == "posts" ? "/feed" : "/feed/#{collection}"
|
46
|
+
return "#{prefix}/#{category}.xml" if category
|
47
|
+
|
48
|
+
collections.dig(collection, "path") || "#{prefix}.xml"
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns a hash representing all collections to be processed and their metadata
|
52
|
+
# in the form of { collection_name => { categories = [...], path = "..." } }
|
53
|
+
def collections
|
54
|
+
return @collections if defined?(@collections)
|
55
|
+
|
56
|
+
@collections = if config["collections"].is_a?(Array)
|
57
|
+
config["collections"].map { |c| [c, {}] }.to_h
|
58
|
+
elsif config["collections"].is_a?(Hash)
|
59
|
+
config["collections"]
|
60
|
+
else
|
61
|
+
{}
|
62
|
+
end
|
63
|
+
|
64
|
+
@collections = normalize_posts_meta(@collections)
|
65
|
+
@collections.each_value do |meta|
|
66
|
+
meta["categories"] = (meta["categories"] || []).to_set
|
67
|
+
end
|
68
|
+
|
69
|
+
@collections
|
70
|
+
end
|
71
|
+
|
72
|
+
# Path to feed.xml template file
|
73
|
+
def feed_source_path
|
74
|
+
@feed_source_path ||= File.expand_path "feed.xml", __dir__
|
75
|
+
end
|
76
|
+
|
77
|
+
def feed_template
|
78
|
+
@feed_template ||= File.read(feed_source_path).gsub(MINIFY_REGEX, "")
|
79
|
+
end
|
80
|
+
|
81
|
+
# Checks if a file already exists in the site source
|
82
|
+
def file_exists?(file_path)
|
83
|
+
File.exist? @site.in_source_dir(file_path)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Generates contents for a file
|
87
|
+
|
88
|
+
def make_page(file_path, collection: "posts", category: nil)
|
89
|
+
PageWithoutAFile.new(@site, __dir__, "", file_path).tap do |file|
|
90
|
+
file.content = feed_template
|
91
|
+
file.data.merge!(
|
92
|
+
"layout" => nil,
|
93
|
+
"sitemap" => false,
|
94
|
+
"xsl" => file_exists?("feed.xslt.xml"),
|
95
|
+
"collection" => collection,
|
96
|
+
"category" => category
|
97
|
+
)
|
98
|
+
file.output
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Special case the "posts" collection, which, for ease of use and backwards
|
103
|
+
# compatability, can be configured via top-level keys or directly as a collection
|
104
|
+
def normalize_posts_meta(hash)
|
105
|
+
hash["posts"] ||= {}
|
106
|
+
hash["posts"]["path"] ||= config["path"]
|
107
|
+
hash["posts"]["categories"] ||= config["categories"]
|
108
|
+
config["path"] ||= hash["posts"]["path"]
|
109
|
+
hash
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JekyllFeed
|
4
|
+
class MetaTag < Liquid::Tag
|
5
|
+
# Use Jekyll's native relative_url filter
|
6
|
+
include Jekyll::Filters::URLFilters
|
7
|
+
|
8
|
+
def render(context)
|
9
|
+
@context = context
|
10
|
+
attrs = attributes.map { |k, v| %(#{k}="#{v}") }.join(" ")
|
11
|
+
"<link #{attrs} />"
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def config
|
17
|
+
@config ||= @context.registers[:site].config
|
18
|
+
end
|
19
|
+
|
20
|
+
def attributes
|
21
|
+
{
|
22
|
+
:type => "application/atom+xml",
|
23
|
+
:rel => "alternate",
|
24
|
+
:href => absolute_url(path),
|
25
|
+
:title => title,
|
26
|
+
}.keep_if { |_, v| v }
|
27
|
+
end
|
28
|
+
|
29
|
+
def path
|
30
|
+
config.dig("feed", "path") || "feed.xml"
|
31
|
+
end
|
32
|
+
|
33
|
+
def title
|
34
|
+
config["title"] || config["name"]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "jekyll-feed/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "rams-jekyll-feed"
|
9
|
+
spec.version = Jekyll::Feed::VERSION
|
10
|
+
spec.authors = ["Ram Patra"]
|
11
|
+
spec.email = ["hi@rampatra.com"]
|
12
|
+
spec.summary = "A Jekyll plugin to generate an Atom feed of your Jekyll posts without the hard limit of 10 posts."
|
13
|
+
spec.homepage = "https://github.com/rampatra/jekyll-feed"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.test_files = spec.files.grep(%r!^spec/!)
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.required_ruby_version = ">= 2.3.0"
|
21
|
+
|
22
|
+
spec.add_dependency "jekyll", ">= 3.7", "< 5.0"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler"
|
25
|
+
spec.add_development_dependency "nokogiri", "~> 1.6"
|
26
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
27
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
28
|
+
spec.add_development_dependency "rubocop-jekyll", "~> 0.5"
|
29
|
+
spec.add_development_dependency "typhoeus", ">= 0.7", "< 2.0"
|
30
|
+
end
|
data/script/bootstrap
ADDED
data/script/cibuild
ADDED
data/script/fmt
ADDED
data/script/release
ADDED
data/script/test
ADDED