pax_global_header00006660000000000000000000000064143224066600014515gustar00rootroot0000000000000052 comment=a58064c68f49e63b0741d368cd95bb35ac2c7c5d jekyll-feed-0.17.0/000077500000000000000000000000001432240666000137755ustar00rootroot00000000000000jekyll-feed-0.17.0/.github/000077500000000000000000000000001432240666000153355ustar00rootroot00000000000000jekyll-feed-0.17.0/.github/workflows/000077500000000000000000000000001432240666000173725ustar00rootroot00000000000000jekyll-feed-0.17.0/.github/workflows/release.yml000066400000000000000000000014311432240666000215340ustar00rootroot00000000000000name: Release Gem on: push: branches: ["master"] paths: ["lib/**/version.rb"] jobs: release: if: "github.repository_owner == 'jekyll'" name: "Release Gem (Ruby ${{ matrix.ruby_version }})" runs-on: "ubuntu-latest" strategy: fail-fast: true matrix: ruby_version: ["2.7"] steps: - name: Checkout Repository uses: actions/checkout@v2 - name: "Set up Ruby ${{ matrix.ruby_version }}" uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby_version }} bundler-cache: true - name: Build and Publish Gem uses: ashmaroli/release-gem@dist with: gemspec_name: "jekyll-feed" env: GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_GEM_PUSH_API_KEY }} jekyll-feed-0.17.0/.github/workflows/ruby.yml000066400000000000000000000010021432240666000210670ustar00rootroot00000000000000name: Continuous Integration on: push: branches: [ master ] pull_request: branches: [ master ] jobs: test: name: 'Ruby ${{ matrix.ruby-version }}' runs-on: ubuntu-latest strategy: matrix: ruby-version: ['2.6', '2.7', '3.0', '3.1'] steps: - uses: actions/checkout@v2 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true - name: Run tests run: bash script/cibuild jekyll-feed-0.17.0/.gitignore000066400000000000000000000003361432240666000157670ustar00rootroot00000000000000/vendor /.bundle/ /.yardoc /Gemfile.lock /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ *.bundle *.so *.o *.a mkmf.log *.gem Gemfile.lock spec/dest .bundle spec/fixtures/.jekyll-metadata spec/fixtures/.jekyll-cache jekyll-feed-0.17.0/.rspec000066400000000000000000000000321432240666000151050ustar00rootroot00000000000000--color --format progress jekyll-feed-0.17.0/.rubocop.yml000066400000000000000000000014211432240666000162450ustar00rootroot00000000000000inherit_from: .rubocop_todo.yml require: rubocop-jekyll inherit_gem: rubocop-jekyll: .rubocop.yml AllCops: TargetRubyVersion: 2.5 SuggestExtensions: false Exclude: - vendor/**/* Layout/LineEndStringConcatenationIndentation: Enabled: true Lint/EmptyInPattern: Enabled: false Metrics/AbcSize: IgnoredMethods: - generate # in generator.rb Naming/InclusiveLanguage: Enabled: false Naming/MemoizedInstanceVariableName: Exclude: - lib/jekyll-feed/page-without-a-file.rb Performance/MapCompact: Enabled: true Performance/RedundantEqualityComparisonBlock: Enabled: true Performance/RedundantSplitRegexpArgument: Enabled: true Style/InPatternThen: Enabled: false Style/MultilineInPatternThen: Enabled: false Style/QuotedSymbols: Enabled: true jekyll-feed-0.17.0/.rubocop_todo.yml000066400000000000000000000037121432240666000172770ustar00rootroot00000000000000# This configuration was generated by # `rubocop --auto-gen-config` # on 2021-09-17 11:44:51 UTC using RuboCop version 1.18.4. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 1 # Cop supports --auto-correct. Layout/EmptyLines: Exclude: - 'spec/jekyll-feed_spec.rb' # Offense count: 15 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. # URISchemes: http, https Layout/LineLength: Max: 144 # Offense count: 3 # Configuration parameters: AllowedMethods. # AllowedMethods: enums Lint/ConstantDefinitionInBlock: Exclude: - 'spec/jekyll-feed_spec.rb' - 'spec/spec_helper.rb' # Offense count: 3 # Cop supports --auto-correct. Performance/RegexpMatch: Exclude: - 'spec/jekyll-feed_spec.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect. Performance/StringInclude: Exclude: - 'spec/jekyll-feed_spec.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys Style/HashSyntax: Exclude: - 'spec/jekyll-feed_spec.rb' # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes Style/StringLiterals: Exclude: - 'spec/jekyll-feed_spec.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma Style/TrailingCommaInHashLiteral: Exclude: - 'spec/jekyll-feed_spec.rb' jekyll-feed-0.17.0/Gemfile000066400000000000000000000005001432240666000152630ustar00rootroot00000000000000# frozen_string_literal: true source "https://rubygems.org" gemspec gem "jekyll", ENV["JEKYLL_VERSION"] if ENV["JEKYLL_VERSION"] gem "kramdown-parser-gfm" if ENV["JEKYLL_VERSION"] == "~> 3.9" gem "rss" if RUBY_VERSION >= "3.0.0" install_if -> { Gem.win_platform? } do gem "tzinfo", "~> 1.2" gem "tzinfo-data" end jekyll-feed-0.17.0/History.markdown000066400000000000000000000116731432240666000172120ustar00rootroot00000000000000## 0.17.0 / 2022-10-14 ### Documentation * Update CI status badge (#363) ### Development Fixes * Add Ruby 3.1 to the CI matrix (#365) ### Minor Enhancements * Allow disabling of jekyll-feed while in development (#370) ## 0.16.0 / 2022-01-03 ### Minor Enhancements * Add support for `page.description` in front matter to become entry `` (#297) ### Bug Fixes * Fold private methods into the `:render` method as local variables (#327) * Check `post.categories` instead of `post.category` (#357) * Switched xml_escape for `` for post content (#332) ### Development Fixes * Add Ruby 3.0 to CI (#337) * Lock RuboCop to v1.18.x (#348) * Add workflow to release gem via GH Action (#355) ### Documentation * Use `.atom` extension in documented examples since we write an Atom feed (#359) ## 0.15.1 / 2020-10-04 ### Bug Fixes * MetaTag: when encoding for XML special characters, handle non-string objects (#326) ## 0.15.0 / 2020-07-10 ### Minor Enhancements * Add support for drafts (#316) ## 0.14.0 / 2020-06-24 ### Minor Enhancements * add support for categories (#153) (#233) * add support for tags (#264) * Make posts limit configurable (#314) * XML escape the title field of feed_meta (#306) ### Bug Fixes * Fix feed link when post title contains HTML (#305) ### Development Fixes * Use Dir to list source files (#309) * Require Ruby >=2.4.0 (#307) ## 0.13.0 / 2019-11-13 ### Minor Enhancements * Excerpt only flag (#287) * Add media:content tag (#290) ### Development Fixes * test: use categories in post (#249) ## 0.12.1 / 2019-03-23 ### Bug Fixes * Re-introduce Ruby 2.3 support and test Jekyll 3.7+ (#272) ## 0.12.0 / 2019-03-21 * Allow Jekyll v4 (still alpha) ### Development Fixes * style: fix offenses in specs (#248) * dev: update CI and style settings (#258) * Enable testing for Windows platform (#265) ## 0.11.0 / 2018-09-09 ### Development Fixes * Require Ruby 2.3 (#222) * Refactor to remove redundant calls and variables (#240) ### Minor Enhancements * Categories and collections (#228) * Remove check for older version of Jekyll (#234) ## 0.10.0 / 2018-06-04 ### Bug Fixes * Escape image URL (#209) ### Development Fixes * Rubocop 0.55 (#223) * Bump Rubocop (#230) ### Minor Enhancements * Support Typhoeus 1.0 (#232) ## 0.9.3 / 2018-02-04 * Define path with __dir (#187) * Bump Ruby for Travis (#188) ### Documentation * Fix: Add note about using plugins instead of gems key (#197) * Add documentation for disabling smartify filter (#205) * Use `https` in more places. (#165) ### Development Fixes * Rubocop: Target Ruby 2.2 (#195) * Test feeds that have a `site.lang` (#164) * Test against Ruby 2.5 (#201) ### Minor Enhancements * fix template for posts with post.lang defined (#168) ## 0.9.3 / 2017-03-28 ## 0.9.1 / 2017-02-17 ### Minor Enhancements * Update feed.xml (#162) ## 0.9.0 / 2017-02-16 ### Minor Enhancements * Use absolute_url to generate the feed_meta url (#150) * Make feed stylesheet optional (#149) * Use new `normalize_whitespace` filter (#143) * Feed entries must contain (#152) * Remove trailing slash from feed ID (#159) ### Development Fixes * Simplify minify regular expression (#141) * Namespace as JekyllFeed (#151) * rubocop -a (#160) ### Bug Fixes * Filter out drafts before limit (#154) ## 0.8.0 / 2016-10-06 * Use filters to clean up Liquid template (#134) ### Minor Enhancements * Don't set @site.config["time"] on feed generation (#138) ### pedantry * Appease Rubocop (#139) ## 0.7.2 / 2016-10-06 * Support `image.path` when `post.image` is an object (#137) ## 0.7.1 / 2016-09-26 * Assign `url_base` before first usage (#133) ## 0.7.0 / 2016-09-06 * Use type="html" to skirt around double escaping problem (#127) ## 0.6.0 / 2016-07-08 * Cleanup `post_author` logic (#113) * Add XML stylesheet example with XSLT (#119) * DRY up and add more doc (#120) * Use smartify filter (#117) ## 0.5.1 / 2016-04-18 * Fix mangling of whitespace when `site.lang` is set (#110) ## 0.5.0 / 2016-04-13 * Consolidate regexps for stripping whitespace (#82) * Only test against Jekyll 3 (#99) * Think about how i18n might work (#75) * Find author by reference (#106) * Drop support for Jekyll 2 (#105) * Add support for post image (#104) ### Minor Enhancements * Use Module#method_defined? (#83) * Use site.title for meta tag if available (#100) ### Development Fixes * Do not require [**jekyll-last-modified-at**](https://github.com/gjtorikian/jekyll-last-modified-at) in tests (#87) * Add Rubocop (#81) * Correct typo in tests (#102) * Simplify testing feed_meta tag (#101) * Quiet known warnings in tests (#103) ## 0.4.0 / 2015-12-30 * Feed uses `site.title`, or `site.name` if `title` doesn't exist (#72) * Replace newlines with spaces in `title` and `summary` elements (#67) * Properly render post content with Jekyll (#73) jekyll-feed-0.17.0/LICENSE.txt000066400000000000000000000021201432240666000156130ustar00rootroot00000000000000Copyright (c) 2015-present Ben Balter and jekyll-feed contributors MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. jekyll-feed-0.17.0/README.md000066400000000000000000000205271432240666000152620ustar00rootroot00000000000000# Jekyll Feed plugin A Jekyll plugin to generate an Atom (RSS-like) feed of your Jekyll posts [![Continuous Integration](https://github.com/jekyll/jekyll-feed/actions/workflows/ruby.yml/badge.svg)](https://github.com/jekyll/jekyll-feed/actions/workflows/ruby.yml) [![Gem Version](https://badge.fury.io/rb/jekyll-feed.svg)](https://badge.fury.io/rb/jekyll-feed) ## Installation Add this line to your site's Gemfile: ```ruby gem 'jekyll-feed' ``` And then add this line to your site's `_config.yml`: ```yml plugins: - jekyll-feed ``` :warning: If you are using Jekyll < 3.5.0 use the `gems` key instead of `plugins`. ## Usage The plugin will automatically generate an Atom feed at `/feed.xml`. ### Optional configuration options The plugin will automatically use any of the following configuration variables, if they are present in your site's `_config.yml` file. * `title` or `name` - The title of the site, e.g., "My awesome site" * `description` - A longer description of what your site is about, e.g., "Where I blog about Jekyll and other awesome things" * `url` - The URL to your site, e.g., `https://example.com`. If none is provided, the plugin will try to use `site.github.url`. * `author` - Global author information (see below) ### Already have a feed path? Do you already have an existing feed someplace other than `/feed.xml`, but are on a host like GitHub Pages that doesn't support machine-friendly redirects? If you simply swap out `jekyll-feed` for your existing template, your existing subscribers won't continue to get updates. Instead, you can specify a non-default path via your site's config. ```yml feed: path: /blog/feed.atom ``` To note, you shouldn't have to do this unless you already have a feed you're using, and you can't or wish not to redirect existing subscribers. ### Optional front matter The plugin will use the following post metadata, automatically generated by Jekyll, which you can override via a post's YAML front matter: * `date` * `title` * `excerpt` * `id` * `category` * `tags` Additionally, the plugin will use the following values, if present in a post's YAML front matter: * `image` - URL of an image that is representative of the post (can also be passed as `image.path`) * `author` - The author of the post, e.g., "Dr. Jekyll". If none is given, feed readers will look to the feed author as defined in `_config.yml`. Like the feed author, this can also be an object or a reference to an author in `_data/authors.yml` (see below). * `description` - A short description of the post. ### Author information *TL;DR: In most cases, put `author: [your name]` in the document's front matter, for sites with multiple authors. If you need something more complicated, read on.* There are several ways to convey author-specific information. Author information is found in the following order of priority: 1. An `author` object, in the documents's front matter, e.g.: ```yml author: twitter: benbalter ``` 2. An `author` object, in the site's `_config.yml`, e.g.: ```yml author: twitter: benbalter ``` 3. `site.data.authors[author]`, if an author is specified in the document's front matter, and a corresponding key exists in `site.data.authors`. E.g., you have the following in the document's front matter: ```yml author: benbalter ``` And you have the following in `_data/authors.yml`: ```yml benbalter: picture: /img/benbalter.png twitter: jekyllrb potus: picture: /img/potus.png twitter: whitehouse ``` In the above example, the author `benbalter`'s Twitter handle will be resolved to `@jekyllrb`. This allows you to centralize author information in a single `_data/authors` file for site with many authors that require more than just the author's username. *Pro-tip: If `authors` is present in the document's front matter as an array (and `author` is not), the plugin will use the first author listed.* 4. An author in the document's front matter (the simplest way), e.g.: ```yml author: benbalter ``` 5. An author in the site's `_config.yml`, e.g.: ```yml author: benbalter ``` ### Meta tags The plugin exposes a helper tag to expose the appropriate meta tags to support automated discovery of your feed. Simply place `{% feed_meta %}` someplace in your template's `` section, to output the necessary metadata. ### SmartyPants The plugin uses [Jekyll's `smartify` filter](https://jekyllrb.com/docs/templates/) for processing the site title and post titles. This will translate plain ASCII punctuation into "smart" typographic punctuation. This will not render or strip any Markdown you may be using in a title. Jekyll's `smartify` filter uses [kramdown](https://kramdown.gettalong.org/options.html) as a processor. Accordingly, if you do not want "smart" typographic punctuation, disabling them in kramdown in your `_config.yml` will disable them in your feed. For example: ```yml kramdown: smart_quotes: apos,apos,quot,quot typographic_symbols: {hellip: ...} ``` ### Custom styling Want to style what your feed looks like in the browser? When a XSLT Styleheet file named `feed.xslt.xml` exists at the root of your repository, a link to this stylesheet is added to the generated feed. ## Why Atom, and not RSS? Great question. In short, Atom is a better format. Think of it like RSS 3.0. For more information, see [this discussion on why we chose Atom over RSS 2.0](https://github.com/jekyll/jekyll-rss-feed/issues/2). ## Categories Jekyll Feed can generate feeds for each category. Simply define which categories you'd like feeds for in your config: ```yml feed: categories: - news - updates ``` ## Posts limit By default the plugin limits the number of posts in the feed to 10. Simply define a new limit in your config: ```yml feed: posts_limit: 20 ``` ## Collections Jekyll Feed can generate feeds for collections other than the Posts collection. This works best for chronological collections (e.g., collections with dates in the filenames). Simply define which collections you'd like feeds for in your config: ```yml feed: collections: - changes ``` By default, collection feeds will be outputted to `/feed/.xml`. If you'd like to customize the output path, specify a collection's custom path as follows: ```yml feed: collections: changes: path: "/changes.atom" ``` Finally, collections can also have category feeds which are outputted as `/feed//.xml`. Specify categories like so: ```yml feed: collections: changes: path: "/changes.atom" categories: - news - updates ``` ## Excerpt Only flag Optional flag `excerpt_only` allows you to exclude post content from the Atom feed. Default value is `false` for backward compatibility. When in `config.yml` is `true` than all posts in feed will be without `` tags. ```yml feed: excerpt_only: true ``` The same flag can be used directly in post file. It will be disable `` tag for selected post. Settings in post file has higher priority than in config file. ## Tags To automatically generate feeds for each tag you apply to your posts you can add a tags setting to your config: ```yml feed: tags: true ``` If there are tags you don't want included in this auto generation you can exclude them ```yml feed: tags: except: - tag-to-exclude - another-tag ``` If you wish to change the location of these auto generated feeds (`/feed/by_tag/.xml` by default) you can provide an alternative folder for them to live in. ```yml feed: tags: path: "alternative/path/for/tags/feeds/" ``` If you only want to generate feeds for a few tags you can also set this. ```yml feed: tags: only: - tag-to-include - another-tag ``` Note that if you include a tag that is excluded a feed will not be generated for it. ## Skip development Use `disable_in_development: true` if you want to turn off feed generation when `jekyll.environment == "development"`, but don't want to remove the plugin (so you don't accidentally commit the removal). Default value is `false`. ```yml feed: disable_in_development: true ``` ## Contributing 1. Fork it (https://github.com/jekyll/jekyll-feed/fork) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create a new Pull Request jekyll-feed-0.17.0/Rakefile000066400000000000000000000002241432240666000154400ustar00rootroot00000000000000# frozen_string_literal: true require "bundler/gem_tasks" require "rspec/core/rake_task" RSpec::Core::RakeTask.new(:spec) task :default => :spec jekyll-feed-0.17.0/appveyor.yml000066400000000000000000000012731432240666000163700ustar00rootroot00000000000000version: "{build}" clone_depth: 5 build: off environment: NOKOGIRI_USE_SYSTEM_LIBRARIES: true JEKYLL_VERSION: "~> 3.9" matrix: - RUBY_FOLDER_VER: "26" JEKYLL_VERSION : "~> 3.8.6" - RUBY_FOLDER_VER: "26" JEKYLL_VERSION : ">= 4.0.0.pre.alpha1" - RUBY_FOLDER_VER: "26" install: - SET PATH=C:\Ruby%RUBY_FOLDER_VER%-x64\bin;%PATH% - bundle install --retry 5 --jobs=%NUMBER_OF_PROCESSORS% --clean --path vendor\bundle test_script: - ruby --version - gem --version - bundler --version - bash ./script/test cache: # If one of the files after the right arrow changes, cache will be invalidated - 'vendor\bundle -> appveyor.yml, Gemfile, jekyll-feed.gemspec' jekyll-feed-0.17.0/jekyll-feed.gemspec000066400000000000000000000021341432240666000175350ustar00rootroot00000000000000# frozen_string_literal: true require_relative "lib/jekyll-feed/version" Gem::Specification.new do |spec| spec.name = "jekyll-feed" spec.version = Jekyll::Feed::VERSION spec.authors = ["Ben Balter"] spec.email = ["ben.balter@github.com"] spec.summary = "A Jekyll plugin to generate an Atom feed of your Jekyll posts" spec.homepage = "https://github.com/jekyll/jekyll-feed" spec.license = "MIT" spec.files = Dir["lib/**/*"] spec.extra_rdoc_files = Dir["README.md", "History.markdown", "LICENSE.txt"] spec.test_files = spec.files.grep(%r!^spec/!) spec.require_paths = ["lib"] spec.required_ruby_version = ">= 2.5.0" spec.add_dependency "jekyll", ">= 3.7", "< 5.0" spec.add_development_dependency "bundler" spec.add_development_dependency "nokogiri", "~> 1.6" spec.add_development_dependency "rake", "~> 12.0" spec.add_development_dependency "rspec", "~> 3.0" spec.add_development_dependency "rubocop-jekyll", "~> 0.12.0" spec.add_development_dependency "typhoeus", ">= 0.7", "< 2.0" end jekyll-feed-0.17.0/lib/000077500000000000000000000000001432240666000145435ustar00rootroot00000000000000jekyll-feed-0.17.0/lib/jekyll-feed.rb000066400000000000000000000004631432240666000172660ustar00rootroot00000000000000# frozen_string_literal: true require "jekyll" require "fileutils" require "jekyll-feed/generator" module JekyllFeed autoload :MetaTag, "jekyll-feed/meta-tag" autoload :PageWithoutAFile, "jekyll-feed/page-without-a-file.rb" end Liquid::Template.register_tag "feed_meta", JekyllFeed::MetaTag jekyll-feed-0.17.0/lib/jekyll-feed/000077500000000000000000000000001432240666000167365ustar00rootroot00000000000000jekyll-feed-0.17.0/lib/jekyll-feed/feed.xml000066400000000000000000000114021432240666000203610ustar00rootroot00000000000000 {% if page.xsl %} {% endif %} Jekyll {{ site.time | date_to_xmlschema }} {{ page.url | absolute_url | xml_escape }} {% assign title = site.title | default: site.name %} {% if page.collection != "posts" %} {% assign collection = page.collection | capitalize %} {% assign title = title | append: " | " | append: collection %} {% endif %} {% if page.category %} {% assign category = page.category | capitalize %} {% assign title = title | append: " | " | append: category %} {% endif %} {% if title %} {{ title | smartify | xml_escape }} {% endif %} {% if site.description %} {{ site.description | xml_escape }} {% endif %} {% if site.author %} {{ site.author.name | default: site.author | xml_escape }} {% if site.author.email %} {{ site.author.email | xml_escape }} {% endif %} {% if site.author.uri %} {{ site.author.uri | xml_escape }} {% endif %} {% endif %} {% if page.tags %} {% assign posts = site.tags[page.tags] %} {% else %} {% assign posts = site[page.collection] %} {% endif %} {% if page.category %} {% assign posts = posts | where: "categories", page.category %} {% endif %} {% unless site.show_drafts %} {% assign posts = posts | where_exp: "post", "post.draft != true" %} {% endunless %} {% assign posts = posts | sort: "date" | reverse %} {% assign posts_limit = site.feed.posts_limit | default: 10 %} {% for post in posts limit: posts_limit %} {% assign post_title = post.title | smartify | strip_html | normalize_whitespace | xml_escape %} {{ post_title }} {{ post.date | date_to_xmlschema }} {{ post.last_modified_at | default: post.date | date_to_xmlschema }} {{ post.id | absolute_url | xml_escape }} {% assign excerpt_only = post.feed.excerpt_only | default: site.feed.excerpt_only %} {% unless excerpt_only %} {% endunless %} {% assign post_author = post.author | default: post.authors[0] | default: site.author %} {% assign post_author = site.data.authors[post_author] | default: post_author %} {% assign post_author_email = post_author.email | default: nil %} {% assign post_author_uri = post_author.uri | default: nil %} {% assign post_author_name = post_author.name | default: post_author %} {{ post_author_name | default: "" | xml_escape }} {% if post_author_email %} {{ post_author_email | xml_escape }} {% endif %} {% if post_author_uri %} {{ post_author_uri | xml_escape }} {% endif %} {% if post.category %} {% elsif post.categories %} {% for category in post.categories %} {% endfor %} {% endif %} {% for tag in post.tags %} {% endfor %} {% assign post_summary = post.description | default: post.excerpt %} {% if post_summary and post_summary != empty %} {% endif %} {% assign post_image = post.image.path | default: post.image %} {% if post_image %} {% unless post_image contains "://" %} {% assign post_image = post_image | absolute_url %} {% endunless %} {% endif %} {% endfor %} jekyll-feed-0.17.0/lib/jekyll-feed/generator.rb000066400000000000000000000115211432240666000212510ustar00rootroot00000000000000# frozen_string_literal: true module JekyllFeed class Generator < Jekyll::Generator safe true priority :lowest # Main plugin action, called by Jekyll-core def generate(site) @site = site if disabled_in_development? Jekyll.logger.info "Jekyll Feed:", "Skipping feed generation in development" return end collections.each do |name, meta| Jekyll.logger.info "Jekyll Feed:", "Generating feed for #{name}" (meta["categories"] + [nil]).each do |category| path = feed_path(:collection => name, :category => category) next if file_exists?(path) @site.pages << make_page(path, :collection => name, :category => category) end end generate_feed_by_tag if config["tags"] && !@site.tags.empty? end private # Matches all whitespace that follows # 1. A '>', which closes an XML tag or # 2. A '}', which closes a Liquid tag # We will strip all of this whitespace to minify the template MINIFY_REGEX = %r!(?<=>|})\s+!.freeze # Returns the plugin's config or an empty hash if not set def config @config ||= @site.config["feed"] || {} end # Determines the destination path of a given feed # # collection - the name of a collection, e.g., "posts" # category - a category within that collection, e.g., "news" # # Will return "/feed.xml", or the config-specified default feed for posts # Will return `/feed/category.xml` for post categories # WIll return `/feed/collection.xml` for other collections # Will return `/feed/collection/category.xml` for other collection categories def feed_path(collection: "posts", category: nil) prefix = collection == "posts" ? "/feed" : "/feed/#{collection}" return "#{prefix}/#{category}.xml" if category collections.dig(collection, "path") || "#{prefix}.xml" end # Returns a hash representing all collections to be processed and their metadata # in the form of { collection_name => { categories = [...], path = "..." } } def collections return @collections if defined?(@collections) @collections = case config["collections"] when Array config["collections"].map { |c| [c, {}] }.to_h when Hash config["collections"] else {} end @collections = normalize_posts_meta(@collections) @collections.each_value do |meta| meta["categories"] = (meta["categories"] || []).to_set end @collections end def generate_feed_by_tag tags_config = config["tags"] tags_config = {} unless tags_config.is_a?(Hash) except = tags_config["except"] || [] only = tags_config["only"] || @site.tags.keys tags_pool = only - except tags_path = tags_config["path"] || "/feed/by_tag/" generate_tag_feed(tags_pool, tags_path) end def generate_tag_feed(tags_pool, tags_path) tags_pool.each do |tag| # allow only tags with basic alphanumeric characters and underscore to keep # feed path simple. next if %r![^a-zA-Z0-9_]!.match?(tag) Jekyll.logger.info "Jekyll Feed:", "Generating feed for posts tagged #{tag}" path = "#{tags_path}#{tag}.xml" next if file_exists?(path) @site.pages << make_page(path, :tags => tag) end end # Path to feed.xml template file def feed_source_path @feed_source_path ||= File.expand_path "feed.xml", __dir__ end def feed_template @feed_template ||= File.read(feed_source_path).gsub(MINIFY_REGEX, "") end # Checks if a file already exists in the site source def file_exists?(file_path) File.exist? @site.in_source_dir(file_path) end # Generates contents for a file def make_page(file_path, collection: "posts", category: nil, tags: nil) PageWithoutAFile.new(@site, __dir__, "", file_path).tap do |file| file.content = feed_template file.data.merge!( "layout" => nil, "sitemap" => false, "xsl" => file_exists?("feed.xslt.xml"), "collection" => collection, "category" => category, "tags" => tags ) file.output end end # Special case the "posts" collection, which, for ease of use and backwards # compatability, can be configured via top-level keys or directly as a collection def normalize_posts_meta(hash) hash["posts"] ||= {} hash["posts"]["path"] ||= config["path"] hash["posts"]["categories"] ||= config["categories"] config["path"] ||= hash["posts"]["path"] hash end def disabled_in_development? config && config["disable_in_development"] && Jekyll.env == "development" end end end jekyll-feed-0.17.0/lib/jekyll-feed/meta-tag.rb000066400000000000000000000014001432240666000207550ustar00rootroot00000000000000# frozen_string_literal: true module JekyllFeed class MetaTag < Liquid::Tag # Use Jekyll's native relative_url filter include Jekyll::Filters::URLFilters def render(context) # Jekyll::Filters::URLFilters requires `@context` to be set in the environment. @context = context config = context.registers[:site].config path = config.dig("feed", "path") || "feed.xml" title = config["title"] || config["name"] attributes = { :type => "application/atom+xml", :rel => "alternate", :href => absolute_url(path), } attributes[:title] = title if title attrs = attributes.map { |k, v| "#{k}=#{v.to_s.encode(:xml => :attr)}" }.join(" ") "" end end end jekyll-feed-0.17.0/lib/jekyll-feed/page-without-a-file.rb000066400000000000000000000002231432240666000230300ustar00rootroot00000000000000# frozen_string_literal: true module JekyllFeed class PageWithoutAFile < Jekyll::Page def read_yaml(*) @data ||= {} end end end jekyll-feed-0.17.0/lib/jekyll-feed/version.rb000066400000000000000000000001341432240666000207460ustar00rootroot00000000000000# frozen_string_literal: true module Jekyll module Feed VERSION = "0.17.0" end end jekyll-feed-0.17.0/script/000077500000000000000000000000001432240666000153015ustar00rootroot00000000000000jekyll-feed-0.17.0/script/bootstrap000077500000000000000000000000351432240666000172420ustar00rootroot00000000000000#! /bin/bash bundle install jekyll-feed-0.17.0/script/cibuild000077500000000000000000000001041432240666000166350ustar00rootroot00000000000000#! /bin/bash set -e script/test script/fmt bundle exec rake build jekyll-feed-0.17.0/script/fmt000077500000000000000000000003401432240666000160120ustar00rootroot00000000000000#!/bin/bash set -e echo "RuboCop $(bundle exec rubocop --version)" bundle exec rubocop -D -E $@ success=$? if ((success != 0)); then echo -e "\nTry running \`script/fmt -a\` to automatically fix errors" fi exit $success jekyll-feed-0.17.0/script/release000077500000000000000000000001251432240666000166450ustar00rootroot00000000000000#!/bin/sh # Tag and push a release. set -e script/cibuild bundle exec rake release jekyll-feed-0.17.0/script/test000077500000000000000000000000541432240666000162050ustar00rootroot00000000000000#!/bin/bash set -ex bundle exec rspec "$@" jekyll-feed-0.17.0/spec/000077500000000000000000000000001432240666000147275ustar00rootroot00000000000000jekyll-feed-0.17.0/spec/fixtures/000077500000000000000000000000001432240666000166005ustar00rootroot00000000000000jekyll-feed-0.17.0/spec/fixtures/_collection/000077500000000000000000000000001432240666000210725ustar00rootroot00000000000000jekyll-feed-0.17.0/spec/fixtures/_collection/2018-01-01-collection-doc.md000066400000000000000000000000471432240666000253370ustar00rootroot00000000000000--- --- Look at me! I'm a collection! jekyll-feed-0.17.0/spec/fixtures/_collection/2018-01-02-collection-category-doc.md000066400000000000000000000001101432240666000271420ustar00rootroot00000000000000--- category: news --- Look at me! I'm a collection doc in a category! jekyll-feed-0.17.0/spec/fixtures/_config.yml000066400000000000000000000001601432240666000207240ustar00rootroot00000000000000timezone: UTC defaults: - scope: path: "" type: pages values: layout: some_default jekyll-feed-0.17.0/spec/fixtures/_data/000077500000000000000000000000001432240666000176505ustar00rootroot00000000000000jekyll-feed-0.17.0/spec/fixtures/_data/authors.yml000066400000000000000000000001401432240666000220530ustar00rootroot00000000000000garthdb: name: Garth twitter: garthdb uri: "http://garthdb.com" email: example@mail.com jekyll-feed-0.17.0/spec/fixtures/_drafts/000077500000000000000000000000001432240666000202225ustar00rootroot00000000000000jekyll-feed-0.17.0/spec/fixtures/_drafts/2015-01-12-a-draft.md000066400000000000000000000000321432240666000231000ustar00rootroot00000000000000--- --- This is a draft. jekyll-feed-0.17.0/spec/fixtures/_layouts/000077500000000000000000000000001432240666000204375ustar00rootroot00000000000000jekyll-feed-0.17.0/spec/fixtures/_layouts/some_default.html000066400000000000000000000001611432240666000237720ustar00rootroot00000000000000--- --- {% feed_meta %} THIS IS MY LAYOUT {{ content }} jekyll-feed-0.17.0/spec/fixtures/_posts/000077500000000000000000000000001432240666000201075ustar00rootroot00000000000000jekyll-feed-0.17.0/spec/fixtures/_posts/2013-12-12-dec-the-second.md000066400000000000000000000001531432240666000242350ustar00rootroot00000000000000--- excerpt: "Foo" image: "/image.png" category: news tags: - test --- # December the twelfth, actually. jekyll-feed-0.17.0/spec/fixtures/_posts/2014-03-02-march-the-second.md000066400000000000000000000002161432240666000245740ustar00rootroot00000000000000--- image: https://cdn.example.org/absolute.png?h=188&w=250 category: news excerpt: "ignore me" description: cool post --- March the second! jekyll-feed-0.17.0/spec/fixtures/_posts/2014-03-04-march-the-fourth.md000066400000000000000000000002551432240666000246350ustar00rootroot00000000000000--- title: Sparkling Title tags: - '"/>' - test image: path: "/object-image.png" categories: updates jekyll --- March the fourth! jekyll-feed-0.17.0/spec/fixtures/_posts/2015-01-18-jekyll-last-modified-at.md000066400000000000000000000002131432240666000260710ustar00rootroot00000000000000--- last_modified_at: 2015-05-12T13:27:59+00:00 tags: - test - fail --- Please don't modify this file. It's modified time is important. jekyll-feed-0.17.0/spec/fixtures/_posts/2015-02-12-strip-newlines.md000066400000000000000000000001041432240666000244330ustar00rootroot00000000000000--- title: The plugin will properly strip newlines. --- jekyll-feed-0.17.0/spec/fixtures/_posts/2015-05-12-liquid.md000066400000000000000000000002271432240666000227500ustar00rootroot00000000000000--- categories: [first, second, third] --- {% capture liquidstring %} Liquid is not rendered. {% endcapture %} {{ liquidstring | replace:'not ','' }} jekyll-feed-0.17.0/spec/fixtures/_posts/2015-05-12-pre.html000066400000000000000000000001141432240666000226060ustar00rootroot00000000000000--- author: Pat lang: en tags: - test ---
Line 1
Line 2
Line 3
jekyll-feed-0.17.0/spec/fixtures/_posts/2015-05-18-author-detail.md000066400000000000000000000002341432240666000242270ustar00rootroot00000000000000--- excerpt: "" author: name: Ben uri: "http://ben.balter.com" email: ben@example.com tags: - test - success --- # December the twelfth, actually. jekyll-feed-0.17.0/spec/fixtures/_posts/2015-08-08-stuck-in-the-middle.html000066400000000000000000000001361432240666000256030ustar00rootroot00000000000000--- tags: - test - fail feed: excerpt_only: true --- This content should not be in feed. jekyll-feed-0.17.0/spec/fixtures/_posts/2016-04-25-author-reference.md000066400000000000000000000000771432240666000247260ustar00rootroot00000000000000--- excerpt: "" author: garthdb --- # April the twenty-fifth? jekyll-feed-0.17.0/spec/fixtures/feed.xslt.xml000066400000000000000000000000001432240666000212040ustar00rootroot00000000000000jekyll-feed-0.17.0/spec/jekyll-feed_spec.rb000066400000000000000000000656251432240666000204770ustar00rootroot00000000000000# frozen_string_literal: true require "spec_helper" describe(JekyllFeed) do let(:overrides) { {} } let(:config) do Jekyll.configuration(Jekyll::Utils.deep_merge_hashes({ "full_rebuild" => true, "source" => source_dir, "destination" => dest_dir, "show_drafts" => false, "url" => "http://example.org", "name" => "My awesome site", "author" => { "name" => "Dr. Jekyll", }, "collections" => { "my_collection" => { "output" => true }, "other_things" => { "output" => false }, }, }, overrides)) end let(:site) { Jekyll::Site.new(config) } let(:contents) { File.read(dest_dir("feed.xml")) } let(:context) { make_context(:site => site) } let(:feed_meta) { Liquid::Template.parse("{% feed_meta %}").render!(context, {}) } let(:jekyll_env) { "development" } before(:each) do allow(Jekyll).to receive(:env).and_return(jekyll_env) site.process end it "has no layout" do expect(contents).not_to match(%r!\ATHIS IS MY LAYOUT!) end it "creates a feed.xml file" do expect(Pathname.new(dest_dir("feed.xml"))).to exist end it "doesn't have multiple new lines or trailing whitespace" do expect(contents).to_not match %r!\s+\n! expect(contents).to_not match %r!\n{2,}! end it "puts all the posts in the feed.xml file" do expect(contents).to match "http://example.org/updates/jekyll/2014/03/04/march-the-fourth.html" expect(contents).to match "http://example.org/news/2014/03/02/march-the-second.html" expect(contents).to match "http://example.org/news/2013/12/12/dec-the-second.html" expect(contents).to match "http://example.org/2015/08/08/stuck-in-the-middle.html" expect(contents).to_not match "http://example.org/2016/02/09/a-draft.html" end it "does not include assets or any static files that aren't .html" do expect(contents).not_to match "http://example.org/images/hubot.png" expect(contents).not_to match "http://example.org/feeds/atom.xml" end it "preserves linebreaks in preformatted text in posts" do expect(contents).to match "Line 1\nLine 2\nLine 3" end it "supports post author name as an object" do expect(contents).to match %r!\s*Ben\s*ben@example\.com\s*http://ben\.balter\.com\s*! end it "supports post author name as a string" do expect(contents).to match %r!\s*Pat\s*! end it "does not output author tag no author is provided" do expect(contents).not_to match %r!\s*\s*! end it "does use author reference with data from _data/authors.yml" do expect(contents).to match %r!\s*Garth\s*example@mail\.com\s*http://garthdb\.com\s*! end it "converts markdown posts to HTML" do expect(contents).to match %r!<\!\[CDATA\[

March the second\!

\]\]! end it "uses last_modified_at where available" do expect(contents).to match %r!2015-05-12T13:27:59\+00:00! end it "replaces newlines in posts to spaces" do expect(contents).to match 'The plugin will properly strip newlines.' end it "strips HTML from link titles" do expect(contents).to match %r!! end it "renders Liquid inside posts" do expect(contents).to match "Liquid is rendered." expect(contents).not_to match "Liquid is not rendered." end context "images" do let(:image1) { 'http://example.org/image.png' } let(:image2) { 'https://cdn.example.org/absolute.png?h=188&w=250' } let(:image3) { 'http://example.org/object-image.png' } it "includes the item image" do expect(contents).to include(%()) expect(contents).to include(%()) expect(contents).to include(%()) end it "included media content for mail templates (Mailchimp)" do expect(contents).to include(%()) expect(contents).to include(%()) expect(contents).to include(%()) end end context "parsing" do let(:feed) { RSS::Parser.parse(contents) } it "outputs an RSS feed" do expect(feed.feed_type).to eql("atom") expect(feed.feed_version).to eql("1.0") expect(feed.encoding).to eql("UTF-8") expect(feed.lang).to be_nil expect(feed.valid?).to eql(true) end it "outputs the link" do expect(feed.link.href).to eql("http://example.org/feed.xml") end it "outputs the generator" do expect(feed.generator.content).to eql("Jekyll") expect(feed.generator.version).to eql(Jekyll::VERSION) end it "includes the items" do expect(feed.items.count).to eql(10) end it "includes item contents" do post = feed.items.last expect(post.title.content).to eql("Dec The Second") expect(post.link.href).to eql("http://example.org/news/2013/12/12/dec-the-second.html") expect(post.published.content).to eql(Time.parse("2013-12-12")) end it "includes the item's excerpt" do post = feed.items.last expect(post.summary.content).to eql("Foo") end it "includes the item's description" do post = feed.items[-2] expect(post.summary.content).to eql("cool post") end it "doesn't include the item's excerpt if blank" do post = feed.items.first expect(post.summary).to be_nil end context "with site.lang set" do lang = "en_US" let(:overrides) { { "lang" => lang } } it "outputs a valid feed" do expect(feed.feed_type).to eql("atom") expect(feed.feed_version).to eql("1.0") expect(feed.encoding).to eql("UTF-8") expect(feed.valid?).to eql(true) end it "outputs the correct language" do expect(feed.lang).to eql(lang) end it "sets the language of entries" do post = feed.items.first expect(post.lang).to eql(lang) end it "renders the feed meta" do expected = %r!! expect(contents).to match(expected) end end context "with site.title set" do let(:site_title) { "My Site Title" } let(:overrides) { { "title" => site_title } } it "uses site.title for the title" do expect(feed.title.content).to eql(site_title) end end context "with site.title set as a non-string value" do class MySiteTitle def to_s "My Dynamic Site Title <&>" end alias_method :to_liquid, :to_s end let(:site_title) { MySiteTitle.new } let(:overrides) { { "title" => site_title } } it "ensures the site.title is the string representation of the object" do expect(feed.title.content).to eql(site_title.to_s.encode(xml: :text)) end end context "with site.name set" do let(:site_name) { "My Site Name" } let(:overrides) { { "name" => site_name } } it "uses site.name for the title" do expect(feed.title.content).to eql(site_name) end end context "with site.name and site.title set" do let(:site_title) { "My Site Title" } let(:site_name) { "My Site Name" } let(:overrides) { { "title" => site_title, "name" => site_name } } it "uses site.title for the title, dropping site.name" do expect(feed.title.content).to eql(site_title) end end context "with site.title has special characters" do let(:site_title) { "My Site Title <&>" } let(:overrides) { { "title" => site_title } } it "uses encoded site.title for the title" do expect(feed.title.content).to eql(site_title.encode(xml: :text)) end end end context "smartify" do let(:site_title) { "Pat's Site" } let(:overrides) { { "title" => site_title } } let(:feed) { RSS::Parser.parse(contents) } it "processes site title with SmartyPants" do expect(feed.title.content).to eql("Pat’s Site") end end context "validation" do it "validates" do skip "Typhoeus couldn't find the 'libcurl' module on Windows" if Gem.win_platform? # See https://validator.w3.org/docs/api.html url = "https://validator.w3.org/feed/check.cgi?output=soap12" response = Typhoeus.post(url, :body => { :rawdata => contents }, :accept_encoding => "gzip") pending "Something went wrong with the W3 validator" unless response.success? result = Nokogiri::XML(response.body) result.remove_namespaces! result.css("warning").each do |warning| # Quiet a warning that results from us passing the feed as a string next if warning.css("text").text =~ %r!Self reference doesn't match document location! # Quiet expected warning that results from blank summary test case next if warning.css("text").text =~ %r!(content|summary) should not be blank! # Quiet expected warning about multiple posts with same updated time next if warning.css("text").text =~ %r!Two entries with the same value for atom:updated! warn "Validation warning: #{warning.css("text").text} on line #{warning.css("line").text} column #{warning.css("column").text}" end errors = result.css("error").map do |error| "Validation error: #{error.css("text").text} on line #{error.css("line").text} column #{error.css("column").text}" end expect(result.css("validity").text).to eql("true"), errors.join("\n") end end context "with a baseurl" do let(:overrides) do { "baseurl" => "/bass" } end it "correctly adds the baseurl to the posts" do expect(contents).to match "http://example.org/bass/updates/jekyll/2014/03/04/march-the-fourth.html" expect(contents).to match "http://example.org/bass/news/2014/03/02/march-the-second.html" expect(contents).to match "http://example.org/bass/news/2013/12/12/dec-the-second.html" end it "renders the feed meta" do expected = 'href="http://example.org/bass/feed.xml"' expect(feed_meta).to include(expected) end end context "feed meta" do it "renders the feed meta" do expected = '' expect(feed_meta).to eql(expected) end context "with a blank site name" do let(:config) do Jekyll.configuration( "source" => source_dir, "destination" => dest_dir, "url" => "http://example.org" ) end it "does not output blank title" do expect(feed_meta).not_to include("title=") end end end context "changing the feed path" do let(:overrides) do { "feed" => { "path" => "atom.xml", }, } end it "should write to atom.xml" do expect(Pathname.new(dest_dir("atom.xml"))).to exist end it "renders the feed meta with custom feed path" do expected = 'href="http://example.org/atom.xml"' expect(feed_meta).to include(expected) end end context "with 'categories' or 'category' or 'tags' key in the front matter" do let(:feed) { RSS::Parser.parse(contents) } let(:entry_with_single_category) { feed.items.find { |i| i.title.content == "March The Second" } } let(:entry_with_multiple_categories) { feed.items.find { |i| i.title.content == "Liquid" } } let(:entry_with_multiple_categories_and_tags) { feed.items.find { |i| i.title.content == "Sparkling Title" } } it "generates the feed correctly" do expect(entry_with_single_category.categories.map(&:term)).to eql(%w(news)) expect(entry_with_multiple_categories.categories.map(&:term)).to eql(%w(first second third)) expect(entry_with_multiple_categories_and_tags.categories.map(&:term)).to eql(["updates", "jekyll", "\"/>", "test"]) end end context "changing the file path via collection meta" do let(:overrides) do { "feed" => { "collections" => { "posts" => { "path" => "atom.xml", }, }, }, } end it "should write to atom.xml" do expect(Pathname.new(dest_dir("atom.xml"))).to exist end it "renders the feed meta with custom feed path" do expected = 'href="http://example.org/atom.xml"' expect(feed_meta).to include(expected) end end context "feed stylesheet" do it "includes the stylesheet" do expect(contents).to include('') end end context "with site.lang set" do let(:overrides) { { "lang" => "en-US" } } it "should set the language" do expect(contents).to match 'type="text/html" hreflang="en-US" />' end end context "with post.lang set" do it "should set the language for that entry" do expect(contents).to match '' expect(contents).to match '' end end context "categories" do context "with top-level post categories" do let(:overrides) do { "feed" => { "categories" => %w(news jekyll) }, } end let(:singular_category_feed) { File.read(dest_dir("feed/news.xml")) } let(:plural_categories_feed) { File.read(dest_dir("feed/jekyll.xml")) } it "outputs the primary feed" do expect(contents).to match "http://example.org/updates/jekyll/2014/03/04/march-the-fourth.html" expect(contents).to match "http://example.org/news/2014/03/02/march-the-second.html" expect(contents).to match "http://example.org/news/2013/12/12/dec-the-second.html" expect(contents).to match "http://example.org/2015/08/08/stuck-in-the-middle.html" expect(contents).to_not match "http://example.org/2016/02/09/a-draft.html" end it "outputs the category feeds" do expect(singular_category_feed).to match 'My awesome site | News' expect(singular_category_feed).to match "http://example.org/news/2014/03/02/march-the-second.html" expect(singular_category_feed).to match "March the second!" expect(plural_categories_feed).to match 'My awesome site | News' expect(plural_categories_feed).to match "http://example.org/updates/jekyll/2014/03/04/march-the-fourth.html" expect(plural_categories_feed).to match "March the fourth!" end end context "with collection-level post categories" do let(:overrides) do { "feed" => { "collections" => { "posts" => { "categories" => ["news"], }, }, }, } end let(:news_feed) { File.read(dest_dir("feed/news.xml")) } it "outputs the primary feed" do expect(contents).to match "http://example.org/updates/jekyll/2014/03/04/march-the-fourth.html" expect(contents).to match "http://example.org/news/2014/03/02/march-the-second.html" expect(contents).to match "http://example.org/news/2013/12/12/dec-the-second.html" expect(contents).to match "http://example.org/2015/08/08/stuck-in-the-middle.html" expect(contents).to_not match "http://example.org/2016/02/09/a-draft.html" end it "outputs the category feed" do expect(news_feed).to match 'My awesome site | News' expect(news_feed).to match "http://example.org/news/2014/03/02/march-the-second.html" expect(news_feed).to match "http://example.org/news/2013/12/12/dec-the-second.html" expect(news_feed).to_not match "http://example.org/updates/jekyll/2014/03/04/march-the-fourth.html" expect(news_feed).to_not match "http://example.org/2015/08/08/stuck-in-the-middle.html" end end end context "collections" do let(:collection_feed) { File.read(dest_dir("feed/collection.xml")) } context "when initialized as an array" do let(:overrides) do { "collections" => { "collection" => { "output" => true, }, }, "feed" => { "collections" => ["collection"] }, } end it "outputs the collection feed" do expect(collection_feed).to match 'My awesome site | Collection' expect(collection_feed).to match "http://example.org/collection/2018-01-01-collection-doc.html" expect(collection_feed).to match "http://example.org/collection/2018-01-02-collection-category-doc.html" expect(collection_feed).to_not match "http://example.org/updates/jekyll/2014/03/04/march-the-fourth.html" expect(collection_feed).to_not match "http://example.org/2015/08/08/stuck-in-the-middle.html" end end context "with categories" do let(:overrides) do { "collections" => { "collection" => { "output" => true, }, }, "feed" => { "collections" => { "collection" => { "categories" => ["news"], }, }, }, } end let(:news_feed) { File.read(dest_dir("feed/collection/news.xml")) } it "outputs the collection category feed" do expect(news_feed).to match 'My awesome site | Collection | News' expect(news_feed).to match "http://example.org/collection/2018-01-02-collection-category-doc.html" expect(news_feed).to_not match "http://example.org/collection/2018-01-01-collection-doc.html" expect(news_feed).to_not match "http://example.org/updates/jekyll/2014/03/04/march-the-fourth.html" expect(news_feed).to_not match "http://example.org/2015/08/08/stuck-in-the-middle.html" end end context "with a custom path" do let(:overrides) do { "collections" => { "collection" => { "output" => true, }, }, "feed" => { "collections" => { "collection" => { "categories" => ["news"], "path" => "custom.xml", }, }, }, } end it "should write to the custom path" do expect(Pathname.new(dest_dir("custom.xml"))).to exist expect(Pathname.new(dest_dir("feed/collection.xml"))).to_not exist expect(Pathname.new(dest_dir("feed/collection/news.xml"))).to exist end end end context "tags" do let(:tags_feed_test) { File.read(dest_dir("feed/by_tag/test.xml")) } let(:tags_feed_fail) { File.read(dest_dir("feed/by_tag/fail.xml")) } let(:tags_feed_success) { File.read(dest_dir("feed/by_tag/success.xml")) } context "do not set tags setting" do it "should not write tags feeds" do expect(Pathname.new(dest_dir("feed/by_tag/test.xml"))).to_not exist expect(Pathname.new(dest_dir("feed/by_tag/fail.xml"))).to_not exist expect(Pathname.new(dest_dir("feed/by_tag/success.xml"))).to_not exist end end context "set tags setting" do let(:overrides) do { "feed" => { "tags" => true }, } end it "should write tags feeds" do expect(Pathname.new(dest_dir("feed/by_tag/test.xml"))).to exist expect(Pathname.new(dest_dir("feed/by_tag/fail.xml"))).to exist expect(Pathname.new(dest_dir("feed/by_tag/success.xml"))).to exist expect(tags_feed_test).to match "/2013/12/12/dec-the-second.html" expect(tags_feed_test).to match "/2014/03/04/march-the-fourth.html" expect(tags_feed_test).to match "/2015/01/18/jekyll-last-modified-at.html" expect(tags_feed_test).to match "/2015/05/12/pre.html" expect(tags_feed_test).to match "/2015/05/18/author-detail.html" expect(tags_feed_test).to match "/2015/08/08/stuck-in-the-middle.html" expect(tags_feed_fail).to match "/2015/01/18/jekyll-last-modified-at.html" expect(tags_feed_fail).to match "/2015/08/08/stuck-in-the-middle.html" expect(tags_feed_fail).to_not match "/2013/12/12/dec-the-second.html" expect(tags_feed_fail).to_not match "/2014/03/02/march-the-second.html" expect(tags_feed_fail).to_not match "/2014/03/04/march-the-fourth.html" expect(tags_feed_fail).to_not match "/2015/05/12/pre.html" expect(tags_feed_fail).to_not match "/2015/05/18/author-detail.html" expect(tags_feed_success).to match "2015/05/18/author-detail.html" expect(tags_feed_success).to_not match "/2013/12/12/dec-the-second.html" expect(tags_feed_success).to_not match "/2014/03/02/march-the-second.html" expect(tags_feed_success).to_not match "/2014/03/04/march-the-fourth.html" expect(tags_feed_success).to_not match "/2015/01/18/jekyll-last-modified-at.html" expect(tags_feed_success).to_not match "/2015/05/12/pre.html" expect(tags_feed_success).to_not match "/2015/08/08/stuck-in-the-middle.html" end end context "set exclusions" do let(:overrides) do { "feed" => { "tags" => { "except" => ["fail"] }, }, } end it "should not write fail feed" do expect(Pathname.new(dest_dir("feed/by_tag/test.xml"))).to exist expect(Pathname.new(dest_dir("feed/by_tag/fail.xml"))).to_not exist expect(Pathname.new(dest_dir("feed/by_tag/success.xml"))).to exist end end context "set inclusions" do let(:overrides) do { "feed" => { "tags" => { "only" => ["success"] }, }, } end it "should not write fail feed" do expect(Pathname.new(dest_dir("feed/by_tag/test.xml"))).to_not exist expect(Pathname.new(dest_dir("feed/by_tag/fail.xml"))).to_not exist expect(Pathname.new(dest_dir("feed/by_tag/success.xml"))).to exist end end context "set alternate path" do let(:overrides) do { "feed" => { "tags" => { "path" => "alternate/path/" }, }, } end it "should write feeds to new path" do expect(Pathname.new(dest_dir("feed/by_tag/test.xml"))).to_not exist expect(Pathname.new(dest_dir("feed/by_tag/fail.xml"))).to_not exist expect(Pathname.new(dest_dir("feed/by_tag/success.xml"))).to_not exist expect(Pathname.new(dest_dir("alternate/path/test.xml"))).to exist expect(Pathname.new(dest_dir("alternate/path/fail.xml"))).to exist expect(Pathname.new(dest_dir("alternate/path/success.xml"))).to exist end context "set to questionable path" do let(:overrides) do { "feed" => { "tags" => { "path" => "../../../../../../../questionable/path/" }, }, } end it "should write feeds to sane paths" do expect(Pathname.new(dest_dir("feed/by_tag/test.xml"))).to_not exist expect(Pathname.new(dest_dir("feed/by_tag/fail.xml"))).to_not exist expect(Pathname.new(dest_dir("feed/by_tag/success.xml"))).to_not exist expect(Pathname.new(dest_dir("questionable/path/test.xml"))).to exist expect(Pathname.new(dest_dir("questionable/path/fail.xml"))).to exist expect(Pathname.new(dest_dir("questionable/path/success.xml"))).to exist end end end end context "excerpt_only flag" do context "backward compatibility for no excerpt_only flag" do it "should be in contents" do expect(contents).to match ' { "excerpt_only" => true } } end it "should not set any contents" do expect(contents).to_not match ' { "excerpt_only" => false } } end it "should be in contents" do expect(contents).to match ' { "excerpt_only" => false } } end it "should not be in contents" do expect(contents).to_not match "This content should not be in feed." end end end context "with feed.posts_limit set to 2" do let(:overrides) do { "feed" => { "posts_limit" => 2 } } end it "puts the latest 2 the posts in the feed.xml file" do expect(contents).to_not match "http://example.org/news/2013/12/12/dec-the-second.html" expect(contents).to_not match "http://example.org/news/2014/03/02/march-the-second.html" expect(contents).to_not match "http://example.org/updates/jekyll/2014/03/04/march-the-fourth.html" expect(contents).to_not match "http://example.org/2015/01/18/jekyll-last-modified-at.html" expect(contents).to_not match "http://example.org/2015/02/12/strip-newlines.html" expect(contents).to_not match "http://example.org/2015/05/12/liquid.html" expect(contents).to_not match "http://example.org/2015/05/12/pre.html" expect(contents).to_not match "http://example.org/2015/05/18/author-detail.html" expect(contents).to match "http://example.org/2015/08/08/stuck-in-the-middle.html" expect(contents).to match "http://example.org/2016/04/25/author-reference.html" end end context "support drafts" do context "with disable show_drafts option" do let(:overrides) do { "show_drafts" => false } end it "should not be draft post" do expect(contents).to_not match "a-draft.html" end end context "with enable show_drafts option" do let(:overrides) do { "show_drafts" => true } end it "should be draft post" do expect(contents).to match "a-draft.html" end end end context "with skip_development" do let(:overrides) do { "feed" => { "disable_in_development" => true }, } end context "in production environment" do let(:jekyll_env) { "production" } it "generates a feed as normal" do expect(Pathname.new(dest_dir("feed.xml"))).to exist end end context "in development environment" do let(:jekyll_env) { "development" } it "does not generate a feed" do expect(Pathname.new(dest_dir("feed.xml"))).not_to exist end end end end jekyll-feed-0.17.0/spec/spec_helper.rb000066400000000000000000000012671432240666000175530ustar00rootroot00000000000000# frozen_string_literal: true require "jekyll" require "typhoeus" unless Gem.win_platform? require "nokogiri" require "rss" require File.expand_path("../lib/jekyll-feed", __dir__) Jekyll.logger.log_level = :error RSpec.configure do |config| config.run_all_when_everything_filtered = true config.filter_run :focus config.order = "random" SOURCE_DIR = File.expand_path("fixtures", __dir__) DEST_DIR = File.expand_path("dest", __dir__) def source_dir(*files) File.join(SOURCE_DIR, *files) end def dest_dir(*files) File.join(DEST_DIR, *files) end def make_context(registers = {}) Liquid::Context.new({}, {}, { :site => site }.merge(registers)) end end