rams-jekyll-feed 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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