diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 7156f41b..79426b97 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['2.6', '2.7', '3.0', '3.1', '3.2', '3.3'] + ruby-version: ['2.6', '2.7', '3.0', '3.1', '3.2', '3.3', '4.0.7'] steps: - uses: actions/checkout@v4 diff --git a/.rubocop.yml b/.rubocop.yml index 80a91934..79cfac9b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,7 +6,7 @@ inherit_gem: rubocop-jekyll: .rubocop.yml AllCops: - TargetRubyVersion: 2.5 + TargetRubyVersion: 2.6 SuggestExtensions: false Exclude: - vendor/**/* @@ -18,7 +18,7 @@ Lint/EmptyInPattern: Enabled: false Metrics/AbcSize: - IgnoredMethods: + AllowedMethods: - generate # in generator.rb Naming/InclusiveLanguage: diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..43beb400 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +4.0.7 diff --git a/Gemfile b/Gemfile index 1a27d6d2..04d9fb53 100644 --- a/Gemfile +++ b/Gemfile @@ -3,9 +3,12 @@ source "https://rubygems.org" gemspec +gem "benchmark" if RUBY_VERSION >= "4.0.0" gem "jekyll", ENV["JEKYLL_VERSION"] if ENV["JEKYLL_VERSION"] gem "kramdown-parser-gfm" if ENV["JEKYLL_VERSION"] == "~> 3.9" +gem "ostruct" if RUBY_VERSION >= "4.0.0" gem "rss" if RUBY_VERSION >= "3.0.0" +gem "tsort" if RUBY_VERSION >= "4.0.0" install_if -> { Gem.win_platform? } do gem "tzinfo", "~> 1.2" diff --git a/jekyll-feed.gemspec b/jekyll-feed.gemspec index 4ffbc6af..de170045 100644 --- a/jekyll-feed.gemspec +++ b/jekyll-feed.gemspec @@ -13,10 +13,9 @@ Gem::Specification.new do |spec| 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.required_ruby_version = ">= 2.6.0" spec.add_dependency "jekyll", ">= 3.7", "< 5.0" @@ -24,6 +23,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency "nokogiri", "~> 1.6" spec.add_development_dependency "rake", "~> 13.0" spec.add_development_dependency "rspec", "~> 3.0" - spec.add_development_dependency "rubocop-jekyll", "~> 0.12.0" + spec.add_development_dependency "rubocop-jekyll", "~> 0.14" + spec.add_development_dependency "simplecov", "~> 0.22" spec.add_development_dependency "typhoeus", ">= 0.7", "< 2.0" end diff --git a/lib/jekyll-feed.rb b/lib/jekyll-feed.rb index 60dd6078..88488827 100644 --- a/lib/jekyll-feed.rb +++ b/lib/jekyll-feed.rb @@ -4,6 +4,9 @@ require "fileutils" require "jekyll-feed/generator" +# Namespace for the jekyll-feed plugin: a Jekyll::Generator that builds Atom +# feed(s) for a site, plus the {% feed_meta %} Liquid tag used to advertise +# them in a page's . module JekyllFeed autoload :MetaTag, "jekyll-feed/meta-tag" autoload :PageWithoutAFile, "jekyll-feed/page-without-a-file.rb" diff --git a/lib/jekyll-feed/generator.rb b/lib/jekyll-feed/generator.rb index 7a959199..21dc67d0 100644 --- a/lib/jekyll-feed/generator.rb +++ b/lib/jekyll-feed/generator.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true module JekyllFeed + # Jekyll::Generator that adds one or more Atom feed pages to the site + # during the build, based on the `feed` key in the site's config + # (collections, categories, tags, and their respective output paths). class Generator < Jekyll::Generator safe true priority :lowest @@ -60,7 +63,7 @@ def collections @collections = case config["collections"] when Array - config["collections"].map { |c| [c, {}] }.to_h + config["collections"].to_h { |c| [c, {}] } when Hash config["collections"] else diff --git a/lib/jekyll-feed/meta-tag.rb b/lib/jekyll-feed/meta-tag.rb index d143c728..1aefa415 100644 --- a/lib/jekyll-feed/meta-tag.rb +++ b/lib/jekyll-feed/meta-tag.rb @@ -1,10 +1,15 @@ # frozen_string_literal: true module JekyllFeed + # Liquid tag that renders the tag used for feed auto-discovery. + # + # Usage in a template: {% feed_meta %} class MetaTag < Liquid::Tag # Use Jekyll's native relative_url filter include Jekyll::Filters::URLFilters + # Renders the tag pointing at the site's feed, + # using `feed.path` (default "feed.xml") and `title`/`name` from the site config. def render(context) # Jekyll::Filters::URLFilters requires `@context` to be set in the environment. @context = context diff --git a/lib/jekyll-feed/page-without-a-file.rb b/lib/jekyll-feed/page-without-a-file.rb index b7d3278a..58a519b5 100644 --- a/lib/jekyll-feed/page-without-a-file.rb +++ b/lib/jekyll-feed/page-without-a-file.rb @@ -1,7 +1,13 @@ # frozen_string_literal: true module JekyllFeed + # A Jekyll::Page whose content and data are set entirely in memory + # (e.g., via `file.content = ...` and `file.data.merge!(...)`) rather + # than read from a file on disk. Used to generate feed.xml without + # requiring a corresponding template file in the site source. class PageWithoutAFile < Jekyll::Page + # Overrides Jekyll::Page#read_yaml, which would otherwise try to read + # and parse front matter from a file that doesn't exist. def read_yaml(*) @data ||= {} end diff --git a/spec/fixtures/feed/by_tag/ghost_tag.xml b/spec/fixtures/feed/by_tag/ghost_tag.xml new file mode 100644 index 00000000..77a18594 --- /dev/null +++ b/spec/fixtures/feed/by_tag/ghost_tag.xml @@ -0,0 +1 @@ +This tag feed file already exists in the source directory and must not be overwritten. diff --git a/spec/fixtures/feed/existing.xml b/spec/fixtures/feed/existing.xml new file mode 100644 index 00000000..8cb0d068 --- /dev/null +++ b/spec/fixtures/feed/existing.xml @@ -0,0 +1 @@ +This feed file already exists in the source directory and must not be overwritten. diff --git a/spec/jekyll-feed_spec.rb b/spec/jekyll-feed_spec.rb index 6f5d58d0..4dbc8d2c 100644 --- a/spec/jekyll-feed_spec.rb +++ b/spec/jekyll-feed_spec.rb @@ -527,6 +527,23 @@ def to_s end end + context "when the feed file already exists in the source directory" do + let(:overrides) do + { + "collections" => { + "existing" => { "output" => true }, + }, + "feed" => { "collections" => ["existing"] }, + } + end + + it "does not overwrite the existing file" do + expect(File.read(dest_dir("feed/existing.xml"))).to eql( + File.read(source_dir("feed/existing.xml")) + ) + end + end + context "with a custom path" do let(:overrides) do { @@ -643,6 +660,18 @@ def to_s end end + context "when the tag feed file already exists in the source directory" do + let(:overrides) do + { "feed" => { "tags" => { "only" => ["ghost_tag"] } } } + end + + it "does not overwrite the existing file" do + expect(File.read(dest_dir("feed/by_tag/ghost_tag.xml"))).to eql( + File.read(source_dir("feed/by_tag/ghost_tag.xml")) + ) + end + end + context "set alternate path" do let(:overrides) do { diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7ea33867..624a6c3d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,11 @@ # frozen_string_literal: true +require "simplecov" +SimpleCov.start do + enable_coverage :branch + add_filter "/spec/" +end + require "jekyll" require "typhoeus" unless Gem.win_platform? require "nokogiri"