New Upstream Release - nanoc

Ready changes

Summary

Merged new upstream version: 4.12.16 (was: 4.12.15).

Diff

diff --git a/.rubocop.yml b/.rubocop.yml
index 4acb4df..e94d14a 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -451,7 +451,7 @@ Style/MapToHash: # new in 1.24
 Style/OpenStructUse: # new in 1.23
   Enabled: true
 
-RSpec/FactoryBot/SyntaxMethods: # new in 2.7
+FactoryBot/SyntaxMethods: # new in 2.7
   Enabled: true
 
 Gemspec/DeprecatedAttributeAssignment: # new in 1.30
@@ -553,7 +553,7 @@ Style/RedundantStringEscape: # new in 1.37
 RSpec/SortMetadata: # new in 2.14
   Enabled: true
 
-RSpec/FactoryBot/ConsistentParenthesesStyle: # new in 2.14
+FactoryBot/ConsistentParenthesesStyle: # new in 2.14
   Enabled: true
 
 RSpec/Rails/InferredSpecType: # new in 2.14
@@ -580,7 +580,7 @@ RSpec/DuplicatedMetadata: # new in 2.16
 RSpec/PendingWithoutReason: # new in 2.16
   Enabled: true
 
-RSpec/FactoryBot/FactoryNameStyle: # new in 2.16
+FactoryBot/FactoryNameStyle: # new in 2.16
   Enabled: true
 
 Gemspec/DevelopmentDependencies: # new in 1.44
@@ -637,6 +637,75 @@ RSpec/Rails/MinitestAssertions: # new in 2.17
 Minitest/NonPublicTestMethod: # new in 0.27
   Enabled: true
 
+Lint/DuplicateMatchPattern: # new in 1.50
+  Enabled: true
+
+Metrics/CollectionLiteralLength: # new in 1.47
+  Enabled: true
+
+Style/DataInheritance: # new in 1.49
+  Enabled: true
+
+Style/DirEmpty: # new in 1.48
+  Enabled: true
+
+Style/ExactRegexpMatch: # new in 1.51
+  Enabled: true
+
+Style/FileEmpty: # new in 1.48
+  Enabled: true
+
+Style/RedundantArrayConstructor: # new in 1.52
+  Enabled: true
+
+Style/RedundantFilterChain: # new in 1.52
+  Enabled: true
+
+Style/RedundantHeredocDelimiterQuotes: # new in 1.45
+  Enabled: true
+
+Style/RedundantLineContinuation: # new in 1.49
+  Enabled: true
+
+Style/RedundantRegexpConstructor: # new in 1.52
+  Enabled: true
+
+Minitest/LifecycleHooksOrder: # new in 0.28
+  Enabled: true
+
+Minitest/ReturnInTestMethod: # new in 0.31
+  Enabled: true
+
+FactoryBot/AssociationStyle: # new in 2.23
+  Enabled: true
+
+FactoryBot/FactoryAssociationWithStrategy: # new in 2.23
+  Enabled: true
+
+FactoryBot/RedundantFactoryOption: # new in 2.23
+  Enabled: true
+
+RSpec/BeEmpty: # new in 2.20
+  Enabled: true
+
+RSpec/ContainExactly: # new in 2.19
+  Enabled: true
+
+RSpec/IndexedLet: # new in 2.20
+  Enabled: true
+
+RSpec/MatchArray: # new in 2.19
+  Enabled: true
+
+RSpec/RedundantAround: # new in 2.19
+  Enabled: true
+
+RSpec/SkipBlockInsideExample: # new in 2.19
+  Enabled: true
+
+RSpec/Rails/TravelAround: # new in 2.19
+  Enabled: true
+
 # ----- TO ENABLE LATER -----
 
 # Valid cops, but fixing the offenses they report is non-trivial.
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 51734dd..4355c3c 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -1,18 +1,11 @@
 # This configuration was generated by
 # `rubocop --auto-gen-config --exclude-limit 300`
-# on 2023-01-29 08:22:38 UTC using RuboCop version 1.44.1.
+# on 2023-06-15 05:08:37 UTC using RuboCop version 1.52.1.
 # 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
-# Configuration parameters: Severity, Include.
-# Include: **/*.gemspec
-Gemspec/RequiredRubyVersion:
-  Exclude:
-    - 'guard-nanoc/guard-nanoc.gemspec'
-
 # Offense count: 1
 Lint/BinaryOperatorWithIdenticalOperands:
   Exclude:
@@ -60,12 +53,14 @@ Lint/HashCompareByIdentity:
   Exclude:
     - 'nanoc/lib/nanoc/filters/sass/importer.rb'
 
-# Offense count: 41
+# Offense count: 58
 Lint/MissingSuper:
   Exclude:
+    - 'nanoc-checking/spec/nanoc/checking/check_spec.rb'
     - 'nanoc-cli/lib/nanoc/cli/compile_listeners/aggregate.rb'
     - 'nanoc-cli/lib/nanoc/cli/compile_listeners/file_action_printer.rb'
     - 'nanoc-cli/lib/nanoc/cli/compile_listeners/timing_recorder.rb'
+    - 'nanoc-cli/spec/nanoc/cli/compile_listeners/abstract_spec.rb'
     - 'nanoc-core/lib/nanoc/core/assertions.rb'
     - 'nanoc-core/lib/nanoc/core/compilation_stages/build_reps.rb'
     - 'nanoc-core/lib/nanoc/core/compilation_stages/calculate_checksums.rb'
@@ -91,9 +86,27 @@ Lint/MissingSuper:
     - 'nanoc-core/lib/nanoc/core/processing_actions/snapshot.rb'
     - 'nanoc-core/lib/nanoc/core/regexp_pattern.rb'
     - 'nanoc-core/lib/nanoc/core/string_pattern.rb'
+    - 'nanoc-core/spec/nanoc/core/compilation_item_rep_view_spec.rb'
+    - 'nanoc-core/spec/nanoc/core/compilation_item_view_spec.rb'
+    - 'nanoc-core/spec/nanoc/core/compilation_phases/cache_spec.rb'
+    - 'nanoc-core/spec/nanoc/core/compilation_stages/compile_reps_spec.rb'
+    - 'nanoc-core/spec/nanoc/core/compilation_stages/preprocess_spec.rb'
+    - 'nanoc-core/spec/nanoc/core/compiler_spec.rb'
+    - 'nanoc-core/spec/nanoc/core/config_view_spec.rb'
+    - 'nanoc-core/spec/nanoc/core/executor_spec.rb'
+    - 'nanoc-core/spec/nanoc/core/filter_spec.rb'
+    - 'nanoc-core/spec/nanoc/core/item_rep_router_spec.rb'
+    - 'nanoc-core/spec/nanoc/core/post_compile_item_rep_view_spec.rb'
+    - 'nanoc-core/spec/nanoc/core/post_compile_item_view_spec.rb'
+    - 'nanoc-core/spec/nanoc/core/support/document_view_examples.rb'
+    - 'nanoc-core/spec/nanoc/core/support/identifiable_collection_view_examples.rb'
+    - 'nanoc-core/spec/nanoc/core/support/item_rep_view_examples.rb'
+    - 'nanoc-core/spec/nanoc/core/support/mutable_document_view_examples.rb'
+    - 'nanoc-spec/lib/nanoc/spec.rb'
     - 'nanoc/lib/nanoc/helpers/breadcrumbs.rb'
     - 'nanoc/lib/nanoc/rule_dsl/action_provider.rb'
-    - 'nanoc/test/data_sources/test_filesystem.rb'
+    - 'nanoc/spec/nanoc/filters/sass_spec.rb'
+    - 'nanoc/spec/nanoc/orig_cli/commands/show_rules_spec.rb'
     - 'nanoc/test/filters/test_slim.rb'
     - 'nanoc/test/filters/test_xsl.rb'
     - 'nanoc/test/helpers/test_blogging.rb'
@@ -101,7 +114,7 @@ Lint/MissingSuper:
     - 'nanoc/test/helpers/test_xml_sitemap.rb'
 
 # Offense count: 2
-# Configuration parameters: AllowedPatterns, IgnoredPatterns.
+# Configuration parameters: AllowedPatterns.
 # AllowedPatterns: (?-mix:(exactly|at_least|at_most)\(\d+\)\.times)
 Lint/UnreachableLoop:
   Exclude:
@@ -111,6 +124,12 @@ Lint/UnreachableLoop:
 Minitest/MultipleAssertions:
   Max: 18
 
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+Minitest/ReturnInTestMethod:
+  Exclude:
+    - 'nanoc/test/orig_cli/commands/test_create_site.rb'
+
 # Offense count: 1
 # Configuration parameters: Severity.
 Minitest/SkipEnsure:
@@ -120,7 +139,7 @@ Minitest/SkipEnsure:
 # Offense count: 9
 # Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
 # SupportedStyles: snake_case, normalcase, non_integer
-# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339
+# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
 Naming/VariableNumber:
   Exclude:
     - 'nanoc-checking/spec/nanoc/checking/check_spec.rb'
@@ -269,7 +288,7 @@ RSpec/EmptyExampleGroup:
     - 'nanoc-core/spec/nanoc/core/outdatedness_rules_spec.rb'
     - 'nanoc/spec/nanoc/helpers/text_spec.rb'
 
-# Offense count: 194
+# Offense count: 195
 # Configuration parameters: CountAsOne.
 RSpec/ExampleLength:
   Max: 29
@@ -308,6 +327,13 @@ RSpec/FilePath:
     - 'nanoc/spec/nanoc/filters/sass_spec.rb'
     - 'nanoc/spec/nanoc/spec_spec.rb'
 
+# Offense count: 8
+# Configuration parameters: Max.
+RSpec/IndexedLet:
+  Exclude:
+    - 'nanoc-core/spec/nanoc/core/aggregate_data_source_spec.rb'
+    - 'nanoc-core/spec/nanoc/core/configuration_spec.rb'
+
 # Offense count: 12
 # Configuration parameters: AssignmentOnly.
 RSpec/InstanceVariable:
@@ -330,11 +356,11 @@ RSpec/MultipleDescribes:
     - 'nanoc/spec/nanoc/rule_dsl/rule_context_spec.rb'
     - 'nanoc/spec/nanoc/rule_dsl/rule_spec.rb'
 
-# Offense count: 408
+# Offense count: 409
 RSpec/MultipleExpectations:
   Max: 19
 
-# Offense count: 731
+# Offense count: 795
 # Configuration parameters: AllowSubject.
 RSpec/MultipleMemoizedHelpers:
   Max: 58
@@ -463,29 +489,6 @@ RSpec/RepeatedExampleGroupDescription:
     - 'nanoc-core/spec/nanoc/core/identifiable_collection_spec.rb'
     - 'nanoc-core/spec/nanoc/core/support/item_rep_view_examples.rb'
 
-# Offense count: 51
-RSpec/ScatteredSetup:
-  Exclude:
-    - 'nanoc-checking/spec/nanoc/checking/checks/external_links_spec.rb'
-    - 'nanoc-cli/spec/nanoc/cli/compile_listeners/file_action_printer_spec.rb'
-    - 'nanoc-cli/spec/nanoc/cli/compile_listeners/timing_recorder_spec.rb'
-    - 'nanoc-core/spec/nanoc/core/compilation_stages/compile_reps_spec.rb'
-    - 'nanoc-core/spec/nanoc/core/config_loader_spec.rb'
-    - 'nanoc-core/spec/nanoc/core/pruner_spec.rb'
-    - 'nanoc-core/spec/nanoc/core/site_loader_spec.rb'
-    - 'nanoc/spec/nanoc/filters/less_spec.rb'
-    - 'nanoc/spec/nanoc/helpers/link_to_spec.rb'
-    - 'nanoc/spec/nanoc/integration/outdatedness_integration_spec.rb'
-    - 'nanoc/spec/nanoc/regressions/gh_1102_spec.rb'
-    - 'nanoc/spec/nanoc/regressions/gh_1216_spec.rb'
-    - 'nanoc/spec/nanoc/regressions/gh_1248_spec.rb'
-    - 'nanoc/spec/nanoc/regressions/gh_1313_spec.rb'
-    - 'nanoc/spec/nanoc/regressions/gh_1319_spec.rb'
-    - 'nanoc/spec/nanoc/regressions/gh_1328_spec.rb'
-    - 'nanoc/spec/nanoc/regressions/gh_776_spec.rb'
-    - 'nanoc/spec/nanoc/regressions/gh_924_spec.rb'
-    - 'nanoc/spec/nanoc/regressions/gh_970b_spec.rb'
-
 # Offense count: 28
 RSpec/StubbedMock:
   Exclude:
@@ -658,6 +661,7 @@ Style/ClassAndModuleChildren:
     - 'nanoc/test/rule_dsl/test_rules_collection.rb'
 
 # Offense count: 2
+# This cop supports unsafe autocorrection (--autocorrect-all).
 Style/CombinableLoops:
   Exclude:
     - 'nanoc-core/lib/nanoc/core/item_rep_router.rb'
diff --git a/Gemfile b/Gemfile
index 545145f..9d06842 100644
--- a/Gemfile
+++ b/Gemfile
@@ -70,10 +70,9 @@ group :plugins do
   gem 'pygments.rb', '~> 2.0', platforms: :ruby
   gem 'rack'
   gem 'rainpress'
-  gem 'rdiscount', '~> 2.2', platforms: :ruby
   gem 'redcarpet', '~> 3.4', platforms: :ruby
   gem 'RedCloth', platforms: :ruby
-  gem 'rouge', '~> 3.1'
+  gem 'rouge', '~> 4.1'
   gem 'ruby-handlebars'
   gem 'rubypants'
   gem 'sass'
@@ -83,4 +82,10 @@ group :plugins do
   gem 'w3c_validators'
   gem 'wdm', '>= 0.1.0' if Gem.win_platform?
   gem 'yuicompressor'
+
+  # TODO: remove
+  # See https://github.com/davidfstr/rdiscount/issues/155
+  unless `clang --version`.match?(/clang version 16/)
+    gem 'rdiscount', '~> 2.2', platforms: :ruby
+  end
 end
diff --git a/debian/changelog b/debian/changelog
index 22825a1..7d63b68 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+nanoc (4.12.16-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+  * Drop patch 0017-Adapt-tests-to-rouge-4.patch, present upstream.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Mon, 21 Aug 2023 05:05:12 -0000
+
 nanoc (4.12.15-2) unstable; urgency=medium
 
   * Upload to unstable
diff --git a/debian/patches/0013-nanoc-cli-skip-failling-test.patch b/debian/patches/0013-nanoc-cli-skip-failling-test.patch
index aae185f..e925f92 100644
--- a/debian/patches/0013-nanoc-cli-skip-failling-test.patch
+++ b/debian/patches/0013-nanoc-cli-skip-failling-test.patch
@@ -8,11 +8,11 @@ feature works.
  nanoc-cli/spec/nanoc/cli/commands/view_spec.rb | 4 ++--
  1 file changed, 2 insertions(+), 2 deletions(-)
 
-diff --git a/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb b/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb
-index e8ff433..d05ae50 100644
---- a/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb
-+++ b/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb
-@@ -26,7 +26,7 @@ describe Nanoc::CLI::Commands::View, fork: true, site: true, stdio: true do
+Index: nanoc.git/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb
++++ nanoc.git/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb
+@@ -26,7 +26,7 @@ describe Nanoc::CLI::Commands::View, for
      end
  
      context 'default configuration' do
@@ -21,7 +21,7 @@ index e8ff433..d05ae50 100644
          File.write('output/index.html', 'Hello there! Nanoc loves you! <3')
          run_nanoc_cmd(['view', '--port', '50385']) do
            expect(Net::HTTP.get('127.0.0.1', '/', 50_385)).to eql('Hello there! Nanoc loves you! <3')
-@@ -46,7 +46,7 @@ describe Nanoc::CLI::Commands::View, fork: true, site: true, stdio: true do
+@@ -46,7 +46,7 @@ describe Nanoc::CLI::Commands::View, for
          File.write('nanoc.yaml', 'index_filenames: [index.xhtml]')
        end
  
diff --git a/debian/patches/0014-nanoc-checking-skip-tests-that-require-w3c_validator.patch b/debian/patches/0014-nanoc-checking-skip-tests-that-require-w3c_validator.patch
index a124065..6209f80 100644
--- a/debian/patches/0014-nanoc-checking-skip-tests-that-require-w3c_validator.patch
+++ b/debian/patches/0014-nanoc-checking-skip-tests-that-require-w3c_validator.patch
@@ -8,21 +8,21 @@ w3c_validators is not packages in Debian yet.
  nanoc-checking/spec/nanoc/checking/checks/html_spec.rb | 2 +-
  2 files changed, 2 insertions(+), 2 deletions(-)
 
-diff --git a/nanoc-checking/spec/nanoc/checking/checks/css_spec.rb b/nanoc-checking/spec/nanoc/checking/checks/css_spec.rb
-index 5ce6aa7..d2706f8 100644
---- a/nanoc-checking/spec/nanoc/checking/checks/css_spec.rb
-+++ b/nanoc-checking/spec/nanoc/checking/checks/css_spec.rb
+Index: nanoc.git/nanoc-checking/spec/nanoc/checking/checks/css_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-checking/spec/nanoc/checking/checks/css_spec.rb
++++ nanoc.git/nanoc-checking/spec/nanoc/checking/checks/css_spec.rb
 @@ -62,4 +62,4 @@ describe Nanoc::Checking::Checks::CSS do
        )
      end
    end
 -end
 +end if false
-diff --git a/nanoc-checking/spec/nanoc/checking/checks/html_spec.rb b/nanoc-checking/spec/nanoc/checking/checks/html_spec.rb
-index 69431db..51f38ef 100644
---- a/nanoc-checking/spec/nanoc/checking/checks/html_spec.rb
-+++ b/nanoc-checking/spec/nanoc/checking/checks/html_spec.rb
-@@ -48,4 +48,4 @@ describe Nanoc::Checking::Checks::HTML do
+Index: nanoc.git/nanoc-checking/spec/nanoc/checking/checks/html_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-checking/spec/nanoc/checking/checks/html_spec.rb
++++ nanoc.git/nanoc-checking/spec/nanoc/checking/checks/html_spec.rb
+@@ -48,4 +48,4 @@ describe Nanoc::Checking::Checks::HTML d
          .to eq('line 1: End tag “h1” seen, but there were open elements.: <h1>Hi!</h2>')
      end
    end
diff --git a/debian/patches/0015-nanoc-core-skip-tests-that-fail-under-autopkgtest.patch b/debian/patches/0015-nanoc-core-skip-tests-that-fail-under-autopkgtest.patch
index 6687579..0c84655 100644
--- a/debian/patches/0015-nanoc-core-skip-tests-that-fail-under-autopkgtest.patch
+++ b/debian/patches/0015-nanoc-core-skip-tests-that-fail-under-autopkgtest.patch
@@ -7,10 +7,10 @@ Subject: nanoc-core: skip tests that fail under autopkgtest
  nanoc-core/spec/nanoc/core/directed_graph_spec.rb | 2 +-
  2 files changed, 2 insertions(+), 2 deletions(-)
 
-diff --git a/nanoc-core/spec/nanoc/core/context_spec.rb b/nanoc-core/spec/nanoc/core/context_spec.rb
-index 5ce50a8..da21689 100644
---- a/nanoc-core/spec/nanoc/core/context_spec.rb
-+++ b/nanoc-core/spec/nanoc/core/context_spec.rb
+Index: nanoc.git/nanoc-core/spec/nanoc/core/context_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-core/spec/nanoc/core/context_spec.rb
++++ nanoc.git/nanoc-core/spec/nanoc/core/context_spec.rb
 @@ -38,7 +38,7 @@ describe Nanoc::Core::Context do
      expect(eval('my_sample()', context.get_binding)).to eq('sample succeeded!')
    end
@@ -20,10 +20,10 @@ index 5ce50a8..da21689 100644
      expect('Nanoc::Core::Context#initialize')
        .to have_correct_yard_examples
        .in_file('nanoc-core/lib/nanoc/core/context.rb')
-diff --git a/nanoc-core/spec/nanoc/core/directed_graph_spec.rb b/nanoc-core/spec/nanoc/core/directed_graph_spec.rb
-index d36d8f9..81dec10 100644
---- a/nanoc-core/spec/nanoc/core/directed_graph_spec.rb
-+++ b/nanoc-core/spec/nanoc/core/directed_graph_spec.rb
+Index: nanoc.git/nanoc-core/spec/nanoc/core/directed_graph_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-core/spec/nanoc/core/directed_graph_spec.rb
++++ nanoc.git/nanoc-core/spec/nanoc/core/directed_graph_spec.rb
 @@ -51,7 +51,7 @@ describe Nanoc::Core::DirectedGraph do
      end
    end
diff --git a/debian/patches/0016-filesystem_spec-skip-tests-that-fail-on-debian.patch b/debian/patches/0016-filesystem_spec-skip-tests-that-fail-on-debian.patch
index dca7ae4..fc2dc46 100644
--- a/debian/patches/0016-filesystem_spec-skip-tests-that-fail-on-debian.patch
+++ b/debian/patches/0016-filesystem_spec-skip-tests-that-fail-on-debian.patch
@@ -6,11 +6,11 @@ Subject: filesystem_spec: skip tests that fail on debian
  nanoc/spec/nanoc/data_sources/filesystem_spec.rb | 2 ++
  1 file changed, 2 insertions(+)
 
-diff --git a/nanoc/spec/nanoc/data_sources/filesystem_spec.rb b/nanoc/spec/nanoc/data_sources/filesystem_spec.rb
-index 8b8d94a..bd69471 100644
---- a/nanoc/spec/nanoc/data_sources/filesystem_spec.rb
-+++ b/nanoc/spec/nanoc/data_sources/filesystem_spec.rb
-@@ -278,6 +278,7 @@ describe Nanoc::DataSources::Filesystem, site: true do
+Index: nanoc.git/nanoc/spec/nanoc/data_sources/filesystem_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc/spec/nanoc/data_sources/filesystem_spec.rb
++++ nanoc.git/nanoc/spec/nanoc/data_sources/filesystem_spec.rb
+@@ -278,6 +278,7 @@ describe Nanoc::DataSources::Filesystem,
        if Nanoc::Core.on_windows?
          skip 'nanoc-live is not currently supported on Windows'
        end
@@ -18,7 +18,7 @@ index 8b8d94a..bd69471 100644
      end
  
      context 'when directory exists' do
-@@ -316,6 +317,7 @@ describe Nanoc::DataSources::Filesystem, site: true do
+@@ -316,6 +317,7 @@ describe Nanoc::DataSources::Filesystem,
  
          subject.stop
        end
diff --git a/debian/patches/0017-Adapt-tests-to-rouge-4.patch b/debian/patches/0017-Adapt-tests-to-rouge-4.patch
deleted file mode 100644
index 1034f5f..0000000
--- a/debian/patches/0017-Adapt-tests-to-rouge-4.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From: Antonio Terceiro <terceiro@debian.org>
-Date: Tue, 13 Jun 2023 15:51:37 -0300
-Subject: Adapt tests to rouge 4
-
----
- Gemfile                                                | 2 +-
- nanoc/spec/nanoc/filters/colorize_syntax/rouge_spec.rb | 4 ++--
- 2 files changed, 3 insertions(+), 3 deletions(-)
-
-diff --git a/Gemfile b/Gemfile
-index 545145f..0d89ca8 100644
---- a/Gemfile
-+++ b/Gemfile
-@@ -73,7 +73,7 @@ group :plugins do
-   gem 'rdiscount', '~> 2.2', platforms: :ruby
-   gem 'redcarpet', '~> 3.4', platforms: :ruby
-   gem 'RedCloth', platforms: :ruby
--  gem 'rouge', '~> 3.1'
-+  gem 'rouge', '~> 4.1'
-   gem 'ruby-handlebars'
-   gem 'rubypants'
-   gem 'sass'
-diff --git a/nanoc/spec/nanoc/filters/colorize_syntax/rouge_spec.rb b/nanoc/spec/nanoc/filters/colorize_syntax/rouge_spec.rb
-index 2b55fcf..e3b9c64 100644
---- a/nanoc/spec/nanoc/filters/colorize_syntax/rouge_spec.rb
-+++ b/nanoc/spec/nanoc/filters/colorize_syntax/rouge_spec.rb
-@@ -86,8 +86,8 @@ describe Nanoc::Filters::ColorizeSyntax, filter: true do
-           let(:output) do
-             <<~EOS
-               before
--              <pre><code class="language-ruby">  <span style="color: #000000;font-weight: bold">def</span> <span style="color: #990000;font-weight: bold">foo</span>
--                <span style="color: #000000;font-weight: bold">end</span></code></pre>
-+              <pre><code class="language-ruby">  <span style="color: #cf222e">def</span> <span style="color: #8250df">foo</span>
-+                <span style="color: #cf222e">end</span></code></pre>
-               after
-             EOS
-           end
diff --git a/debian/patches/add-live-command.patch b/debian/patches/add-live-command.patch
index 71a2d64..a9330a0 100644
--- a/debian/patches/add-live-command.patch
+++ b/debian/patches/add-live-command.patch
@@ -10,10 +10,10 @@ Forwarded: no
  nanoc/bin/nanoc | 5 +++++
  1 file changed, 5 insertions(+)
 
-diff --git a/nanoc/bin/nanoc b/nanoc/bin/nanoc
-index 33bbdad..88f32fc 100755
---- a/nanoc/bin/nanoc
-+++ b/nanoc/bin/nanoc
+Index: nanoc.git/nanoc/bin/nanoc
+===================================================================
+--- nanoc.git.orig/nanoc/bin/nanoc
++++ nanoc.git/nanoc/bin/nanoc
 @@ -9,6 +9,11 @@ begin
  rescue LoadError
  end
diff --git a/debian/patches/encoding.patch b/debian/patches/encoding.patch
index ec9d231..0ca3c32 100644
--- a/debian/patches/encoding.patch
+++ b/debian/patches/encoding.patch
@@ -11,10 +11,10 @@ Forwarded: not-needed
  nanoc/test/orig_cli/commands/test_create_site.rb   | 2 ++
  4 files changed, 8 insertions(+)
 
-diff --git a/nanoc-cli/spec/nanoc/cli/commands/shell_spec.rb b/nanoc-cli/spec/nanoc/cli/commands/shell_spec.rb
-index 5840a20..832f73b 100644
---- a/nanoc-cli/spec/nanoc/cli/commands/shell_spec.rb
-+++ b/nanoc-cli/spec/nanoc/cli/commands/shell_spec.rb
+Index: nanoc.git/nanoc-cli/spec/nanoc/cli/commands/shell_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-cli/spec/nanoc/cli/commands/shell_spec.rb
++++ nanoc.git/nanoc-cli/spec/nanoc/cli/commands/shell_spec.rb
 @@ -1,5 +1,7 @@
  # frozen_string_literal: true
  
@@ -23,10 +23,10 @@ index 5840a20..832f73b 100644
  describe Nanoc::CLI::Commands::Shell, site: true, stdio: true do
    describe '#run' do
      before do
-diff --git a/nanoc-live/spec/nanoc/live/live_recompiler_spec.rb b/nanoc-live/spec/nanoc/live/live_recompiler_spec.rb
-index 801eff5..846106c 100644
---- a/nanoc-live/spec/nanoc/live/live_recompiler_spec.rb
-+++ b/nanoc-live/spec/nanoc/live/live_recompiler_spec.rb
+Index: nanoc.git/nanoc-live/spec/nanoc/live/live_recompiler_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-live/spec/nanoc/live/live_recompiler_spec.rb
++++ nanoc.git/nanoc-live/spec/nanoc/live/live_recompiler_spec.rb
 @@ -1,5 +1,7 @@
  # frozen_string_literal: true
  
@@ -35,10 +35,10 @@ index 801eff5..846106c 100644
  describe Nanoc::Live::LiveRecompiler, fork: true, site: true, stdio: true do
    before do
      Nanoc::CLI::ErrorHandler.enable
-diff --git a/nanoc/spec/contributors_spec.rb b/nanoc/spec/contributors_spec.rb
-index 8c6fa91..e904330 100644
---- a/nanoc/spec/contributors_spec.rb
-+++ b/nanoc/spec/contributors_spec.rb
+Index: nanoc.git/nanoc/spec/contributors_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc/spec/contributors_spec.rb
++++ nanoc.git/nanoc/spec/contributors_spec.rb
 @@ -1,5 +1,7 @@
  # frozen_string_literal: true
  
@@ -47,10 +47,10 @@ index 8c6fa91..e904330 100644
  describe 'list of contributors in README', chdir: false do
    let(:contributors_in_readme) do
      File.readlines('../README.md').last.chomp("\n").split(', ')
-diff --git a/nanoc/test/orig_cli/commands/test_create_site.rb b/nanoc/test/orig_cli/commands/test_create_site.rb
-index 2281642..c4e506c 100644
---- a/nanoc/test/orig_cli/commands/test_create_site.rb
-+++ b/nanoc/test/orig_cli/commands/test_create_site.rb
+Index: nanoc.git/nanoc/test/orig_cli/commands/test_create_site.rb
+===================================================================
+--- nanoc.git.orig/nanoc/test/orig_cli/commands/test_create_site.rb
++++ nanoc.git/nanoc/test/orig_cli/commands/test_create_site.rb
 @@ -1,5 +1,7 @@
  # frozen_string_literal: true
  
diff --git a/debian/patches/failing-tests-ipv6-only.patch b/debian/patches/failing-tests-ipv6-only.patch
index 453ac90..3fdebed 100644
--- a/debian/patches/failing-tests-ipv6-only.patch
+++ b/debian/patches/failing-tests-ipv6-only.patch
@@ -9,11 +9,11 @@ Last-Update: 2020-11-18
  nanoc-live/spec/nanoc/live/command_runners/live_spec.rb | 6 +++---
  1 file changed, 3 insertions(+), 3 deletions(-)
 
-diff --git a/nanoc-live/spec/nanoc/live/command_runners/live_spec.rb b/nanoc-live/spec/nanoc/live/command_runners/live_spec.rb
-index c413ff7..0f8ea72 100644
---- a/nanoc-live/spec/nanoc/live/command_runners/live_spec.rb
-+++ b/nanoc-live/spec/nanoc/live/command_runners/live_spec.rb
-@@ -31,7 +31,7 @@ describe Nanoc::Live::CommandRunners::Live, fork: true, site: true, stdio: true
+Index: nanoc.git/nanoc-live/spec/nanoc/live/command_runners/live_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-live/spec/nanoc/live/command_runners/live_spec.rb
++++ nanoc.git/nanoc-live/spec/nanoc/live/command_runners/live_spec.rb
+@@ -31,7 +31,7 @@ describe Nanoc::Live::CommandRunners::Li
      end
    end
  
@@ -22,7 +22,7 @@ index c413ff7..0f8ea72 100644
      run_cmd do
        File.write('content/lol.html', 'hej')
        sleep_until { File.file?('output/lol.html') }
-@@ -43,7 +43,7 @@ describe Nanoc::Live::CommandRunners::Live, fork: true, site: true, stdio: true
+@@ -43,7 +43,7 @@ describe Nanoc::Live::CommandRunners::Li
      end
    end
  
@@ -31,7 +31,7 @@ index c413ff7..0f8ea72 100644
      run_cmd do
        File.write('content/lol.html', 'hej')
        sleep_until { File.file?('output/lol.html') }
-@@ -55,7 +55,7 @@ describe Nanoc::Live::CommandRunners::Live, fork: true, site: true, stdio: true
+@@ -55,7 +55,7 @@ describe Nanoc::Live::CommandRunners::Li
      end
    end
  
diff --git a/debian/patches/filter_out_nil_gemspec.patch b/debian/patches/filter_out_nil_gemspec.patch
index 23d1c60..3292a4f 100644
--- a/debian/patches/filter_out_nil_gemspec.patch
+++ b/debian/patches/filter_out_nil_gemspec.patch
@@ -12,11 +12,11 @@ selecting explicitly the non nil ones fixes the problem for now
  nanoc-cli/lib/nanoc/cli/error_handler.rb | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
 
-diff --git a/nanoc-cli/lib/nanoc/cli/error_handler.rb b/nanoc-cli/lib/nanoc/cli/error_handler.rb
-index dc540d7..cef3bf6 100644
---- a/nanoc-cli/lib/nanoc/cli/error_handler.rb
-+++ b/nanoc-cli/lib/nanoc/cli/error_handler.rb
-@@ -159,7 +159,7 @@ module Nanoc::CLI
+Index: nanoc.git/nanoc-cli/lib/nanoc/cli/error_handler.rb
+===================================================================
+--- nanoc.git.orig/nanoc-cli/lib/nanoc/cli/error_handler.rb
++++ nanoc.git/nanoc-cli/lib/nanoc/cli/error_handler.rb
+@@ -161,7 +161,7 @@ module Nanoc::CLI
      # @return [Hash<String, Array>] A hash containing the gem names as keys and gem versions as value
      def gems_and_versions
        gems = {}
diff --git a/debian/patches/missing_pathname_require.patch b/debian/patches/missing_pathname_require.patch
index 02a911e..11b37c4 100644
--- a/debian/patches/missing_pathname_require.patch
+++ b/debian/patches/missing_pathname_require.patch
@@ -7,10 +7,10 @@ Last-Update: 2020-04-12
  nanoc-core/lib/nanoc/core/pruner.rb | 2 ++
  1 file changed, 2 insertions(+)
 
-diff --git a/nanoc-core/lib/nanoc/core/pruner.rb b/nanoc-core/lib/nanoc/core/pruner.rb
-index fa939c6..17aa592 100644
---- a/nanoc-core/lib/nanoc/core/pruner.rb
-+++ b/nanoc-core/lib/nanoc/core/pruner.rb
+Index: nanoc.git/nanoc-core/lib/nanoc/core/pruner.rb
+===================================================================
+--- nanoc.git.orig/nanoc-core/lib/nanoc/core/pruner.rb
++++ nanoc.git/nanoc-core/lib/nanoc/core/pruner.rb
 @@ -1,5 +1,7 @@
  # frozen_string_literal: true
  
diff --git a/debian/patches/nanoc-live_missing_loader.patch b/debian/patches/nanoc-live_missing_loader.patch
index 45ade7b..52df631 100644
--- a/debian/patches/nanoc-live_missing_loader.patch
+++ b/debian/patches/nanoc-live_missing_loader.patch
@@ -9,19 +9,18 @@ Subject: nanoc-live_missing_loader
  2 files changed, 4 insertions(+)
  create mode 100644 nanoc-live/lib/nanoc-live.rb
 
-diff --git a/nanoc-live/lib/nanoc-live.rb b/nanoc-live/lib/nanoc-live.rb
-new file mode 100644
-index 0000000..07a9ab0
+Index: nanoc.git/nanoc-live/lib/nanoc-live.rb
+===================================================================
 --- /dev/null
-+++ b/nanoc-live/lib/nanoc-live.rb
++++ nanoc.git/nanoc-live/lib/nanoc-live.rb
 @@ -0,0 +1,3 @@
 +# frozen_string_literal: true
 +
 +require_relative 'nanoc/live'
-diff --git a/nanoc-live/nanoc-live.manifest b/nanoc-live/nanoc-live.manifest
-index e563ff3..b556b63 100644
---- a/nanoc-live/nanoc-live.manifest
-+++ b/nanoc-live/nanoc-live.manifest
+Index: nanoc.git/nanoc-live/nanoc-live.manifest
+===================================================================
+--- nanoc.git.orig/nanoc-live/nanoc-live.manifest
++++ nanoc.git/nanoc-live/nanoc-live.manifest
 @@ -1,6 +1,7 @@
  NEWS.md
  README.md
diff --git a/debian/patches/no_privacy_breach.patch b/debian/patches/no_privacy_breach.patch
index e085bbc..a42378b 100644
--- a/debian/patches/no_privacy_breach.patch
+++ b/debian/patches/no_privacy_breach.patch
@@ -14,10 +14,10 @@ Last-Update: 2018-10-01
  README.md | 7 -------
  1 file changed, 7 deletions(-)
 
-diff --git a/README.md b/README.md
-index db5a45b..e91a9d3 100644
---- a/README.md
-+++ b/README.md
+Index: nanoc.git/README.md
+===================================================================
+--- nanoc.git.orig/README.md
++++ nanoc.git/README.md
 @@ -1,10 +1,3 @@
 -[![Gem version](https://img.shields.io/gem/v/nanoc.svg)](http://rubygems.org/gems/nanoc)
 -[![Gem downloads](https://img.shields.io/gem/dt/nanoc.svg)](http://rubygems.org/gems/nanoc)
diff --git a/debian/patches/no_simplecov.patch b/debian/patches/no_simplecov.patch
index b0dfe5e..228f1da 100644
--- a/debian/patches/no_simplecov.patch
+++ b/debian/patches/no_simplecov.patch
@@ -9,10 +9,10 @@ Last-Update: 2018-11-06
  nanoc/test/helper.rb                 | 3 ---
  2 files changed, 6 deletions(-)
 
-diff --git a/common/spec/spec_helper_head_core.rb b/common/spec/spec_helper_head_core.rb
-index 8b26edd..e1e3356 100644
---- a/common/spec/spec_helper_head_core.rb
-+++ b/common/spec/spec_helper_head_core.rb
+Index: nanoc.git/common/spec/spec_helper_head_core.rb
+===================================================================
+--- nanoc.git.orig/common/spec/spec_helper_head_core.rb
++++ nanoc.git/common/spec/spec_helper_head_core.rb
 @@ -1,8 +1,5 @@
  # frozen_string_literal: true
  
@@ -22,10 +22,10 @@ index 8b26edd..e1e3356 100644
  ENV['NANOC_DEV_MODE'] = 'true'
  
  require 'fuubar'
-diff --git a/nanoc/test/helper.rb b/nanoc/test/helper.rb
-index 3bd6a0f..6236a3e 100644
---- a/nanoc/test/helper.rb
-+++ b/nanoc/test/helper.rb
+Index: nanoc.git/nanoc/test/helper.rb
+===================================================================
+--- nanoc.git.orig/nanoc/test/helper.rb
++++ nanoc.git/nanoc/test/helper.rb
 @@ -2,9 +2,6 @@
  
  $VERBOSE = false
diff --git a/debian/patches/rspec_ambiguous_metadata.patch b/debian/patches/rspec_ambiguous_metadata.patch
index 19abb80..28cb92b 100644
--- a/debian/patches/rspec_ambiguous_metadata.patch
+++ b/debian/patches/rspec_ambiguous_metadata.patch
@@ -8,11 +8,11 @@ Last-Update: 2018-12-10
  nanoc/spec/nanoc/data_sources/filesystem_spec.rb | 4 ++--
  1 file changed, 2 insertions(+), 2 deletions(-)
 
-diff --git a/nanoc/spec/nanoc/data_sources/filesystem_spec.rb b/nanoc/spec/nanoc/data_sources/filesystem_spec.rb
-index ccea88e..8b8d94a 100644
---- a/nanoc/spec/nanoc/data_sources/filesystem_spec.rb
-+++ b/nanoc/spec/nanoc/data_sources/filesystem_spec.rb
-@@ -122,7 +122,7 @@ describe Nanoc::DataSources::Filesystem, site: true do
+Index: nanoc.git/nanoc/spec/nanoc/data_sources/filesystem_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc/spec/nanoc/data_sources/filesystem_spec.rb
++++ nanoc.git/nanoc/spec/nanoc/data_sources/filesystem_spec.rb
+@@ -122,7 +122,7 @@ describe Nanoc::DataSources::Filesystem,
            File.write('foo/a.yaml', 'title: Aaah')
          end
  
@@ -21,7 +21,7 @@ index ccea88e..8b8d94a 100644
            expect { subject }
              .to raise_error(
                Nanoc::DataSources::Filesystem::AmbiguousMetadataAssociationError,
-@@ -166,7 +166,7 @@ describe Nanoc::DataSources::Filesystem, site: true do
+@@ -166,7 +166,7 @@ describe Nanoc::DataSources::Filesystem,
            File.write('foo/a.yaml', 'title: Aaah')
          end
  
diff --git a/debian/patches/rspec_nanoc_cli.patch b/debian/patches/rspec_nanoc_cli.patch
index 8f69797..c1d0473 100644
--- a/debian/patches/rspec_nanoc_cli.patch
+++ b/debian/patches/rspec_nanoc_cli.patch
@@ -8,11 +8,11 @@ Last-Update: 2018-12-10
  nanoc-cli/spec/nanoc/cli/error_handler_spec.rb    | 2 +-
  2 files changed, 2 insertions(+), 2 deletions(-)
 
-diff --git a/nanoc-cli/spec/nanoc/cli/commands/compile_spec.rb b/nanoc-cli/spec/nanoc/cli/commands/compile_spec.rb
-index 00d5cc7..244fdbd 100644
---- a/nanoc-cli/spec/nanoc/cli/commands/compile_spec.rb
-+++ b/nanoc-cli/spec/nanoc/cli/commands/compile_spec.rb
-@@ -56,7 +56,7 @@ describe Nanoc::CLI::Commands::Compile, site: true, stdio: true do
+Index: nanoc.git/nanoc-cli/spec/nanoc/cli/commands/compile_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-cli/spec/nanoc/cli/commands/compile_spec.rb
++++ nanoc.git/nanoc-cli/spec/nanoc/cli/commands/compile_spec.rb
+@@ -56,7 +56,7 @@ describe Nanoc::CLI::Commands::Compile,
      end
  
      describe '--watch', fork: true do
@@ -21,11 +21,11 @@ index 00d5cc7..244fdbd 100644
          pipe_stdout_read, pipe_stdout_write = IO.pipe
          pid = fork do
            trap(:INT) { exit(0) }
-diff --git a/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb b/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
-index bc9db58..b9a865d 100644
---- a/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
-+++ b/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
-@@ -106,7 +106,7 @@ describe Nanoc::CLI::ErrorHandler, stdio: true do
+Index: nanoc.git/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
++++ nanoc.git/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
+@@ -106,7 +106,7 @@ describe Nanoc::CLI::ErrorHandler, stdio
            return e
          end
  
diff --git a/debian/patches/rspec_readme_gem_manifest.patch b/debian/patches/rspec_readme_gem_manifest.patch
index 54aefac..247a13f 100644
--- a/debian/patches/rspec_readme_gem_manifest.patch
+++ b/debian/patches/rspec_readme_gem_manifest.patch
@@ -9,11 +9,11 @@ Last-Update: 2018-12-10
  nanoc/spec/manifest_spec.rb     | 10 +++++-----
  3 files changed, 8 insertions(+), 8 deletions(-)
 
-diff --git a/nanoc/spec/contributors_spec.rb b/nanoc/spec/contributors_spec.rb
-index e904330..4d4b1d0 100644
---- a/nanoc/spec/contributors_spec.rb
-+++ b/nanoc/spec/contributors_spec.rb
-@@ -11,12 +11,12 @@ describe 'list of contributors in README', chdir: false do
+Index: nanoc.git/nanoc/spec/contributors_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc/spec/contributors_spec.rb
++++ nanoc.git/nanoc/spec/contributors_spec.rb
+@@ -11,12 +11,12 @@ describe 'list of contributors in README
      File.read('NEWS.md').scan(/\[[^\]]+\]$/).map { |s| s[1..-2].split(', ') }.flatten
    end
  
@@ -28,11 +28,11 @@ index e904330..4d4b1d0 100644
      expect(contributors_in_readme).to be_humanly_sorted
    end
  end
-diff --git a/nanoc/spec/gem_spec.rb b/nanoc/spec/gem_spec.rb
-index 09ecb7c..9d5dc58 100644
---- a/nanoc/spec/gem_spec.rb
-+++ b/nanoc/spec/gem_spec.rb
-@@ -11,7 +11,7 @@ describe 'nanoc.gem', chdir: false, stdio: true do
+Index: nanoc.git/nanoc/spec/gem_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc/spec/gem_spec.rb
++++ nanoc.git/nanoc/spec/gem_spec.rb
+@@ -11,7 +11,7 @@ describe 'nanoc.gem', chdir: false, stdi
      Dir['*.gem'].each { |f| FileUtils.rm(f) }
    end
  
@@ -41,10 +41,10 @@ index 09ecb7c..9d5dc58 100644
      expect { subject }
        .to change { Dir['*.gem'] }
        .from([])
-diff --git a/nanoc/spec/manifest_spec.rb b/nanoc/spec/manifest_spec.rb
-index d69ca8a..b88c0f7 100644
---- a/nanoc/spec/manifest_spec.rb
-+++ b/nanoc/spec/manifest_spec.rb
+Index: nanoc.git/nanoc/spec/manifest_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc/spec/manifest_spec.rb
++++ nanoc.git/nanoc/spec/manifest_spec.rb
 @@ -1,7 +1,7 @@
  # frozen_string_literal: true
  
diff --git a/debian/patches/series b/debian/patches/series
index b28258e..2dbbf23 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -14,4 +14,3 @@ failing-tests-ipv6-only.patch
 0014-nanoc-checking-skip-tests-that-require-w3c_validator.patch
 0015-nanoc-core-skip-tests-that-fail-under-autopkgtest.patch
 0016-filesystem_spec-skip-tests-that-fail-on-debian.patch
-0017-Adapt-tests-to-rouge-4.patch
diff --git a/debian/patches/skip-failing-tests.patch b/debian/patches/skip-failing-tests.patch
index c8f22da..0e46bf9 100644
--- a/debian/patches/skip-failing-tests.patch
+++ b/debian/patches/skip-failing-tests.patch
@@ -15,11 +15,11 @@ Last-Update: 2020-04-07
  nanoc/test/filters/test_erubi.rb               |  2 +-
  9 files changed, 39 insertions(+), 39 deletions(-)
 
-diff --git a/nanoc-cli/spec/gem_spec.rb b/nanoc-cli/spec/gem_spec.rb
-index df6af0e..2204afc 100644
---- a/nanoc-cli/spec/gem_spec.rb
-+++ b/nanoc-cli/spec/gem_spec.rb
-@@ -11,7 +11,7 @@ describe 'nanoc-cli.gem', chdir: false, stdio: true do
+Index: nanoc.git/nanoc-cli/spec/gem_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-cli/spec/gem_spec.rb
++++ nanoc.git/nanoc-cli/spec/gem_spec.rb
+@@ -11,7 +11,7 @@ describe 'nanoc-cli.gem', chdir: false,
      Dir['*.gem'].each { |f| FileUtils.rm(f) }
    end
  
@@ -28,10 +28,10 @@ index df6af0e..2204afc 100644
      expect { subject }
        .to change { Dir['*.gem'] }
        .from([])
-diff --git a/nanoc-cli/spec/manifest_spec.rb b/nanoc-cli/spec/manifest_spec.rb
-index 5d99076..ac1d331 100644
---- a/nanoc-cli/spec/manifest_spec.rb
-+++ b/nanoc-cli/spec/manifest_spec.rb
+Index: nanoc.git/nanoc-cli/spec/manifest_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-cli/spec/manifest_spec.rb
++++ nanoc.git/nanoc-cli/spec/manifest_spec.rb
 @@ -1,7 +1,7 @@
  # frozen_string_literal: true
  
@@ -45,11 +45,11 @@ index 5d99076..ac1d331 100644
 +#    expect('nanoc-cli').to have_a_valid_manifest
 +#  end
 +#end
-diff --git a/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb b/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb
-index 9d50028..e8ff433 100644
---- a/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb
-+++ b/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb
-@@ -61,7 +61,7 @@ describe Nanoc::CLI::Commands::View, fork: true, site: true, stdio: true do
+Index: nanoc.git/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb
++++ nanoc.git/nanoc-cli/spec/nanoc/cli/commands/view_spec.rb
+@@ -61,7 +61,7 @@ describe Nanoc::CLI::Commands::View, for
        end
      end
  
@@ -58,11 +58,11 @@ index 9d50028..e8ff433 100644
        FileUtils.rm_rf('output')
        run_nanoc_cmd(['view', '--port', '50385', '--live-reload']) do
          expect(Net::HTTP.get('127.0.0.1', '/', 50_385)).to eql("File not found: /\n")
-diff --git a/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb b/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
-index b9a865d..63cd1bd 100644
---- a/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
-+++ b/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
-@@ -72,7 +72,7 @@ describe Nanoc::CLI::ErrorHandler, stdio: true do
+Index: nanoc.git/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
++++ nanoc.git/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
+@@ -72,7 +72,7 @@ describe Nanoc::CLI::ErrorHandler, stdio
        context 'exit on error' do
          let(:exit_on_error) { true }
  
@@ -71,7 +71,7 @@ index b9a865d..63cd1bd 100644
            expect { subject }.to raise_error(SystemExit)
          end
        end
-@@ -80,7 +80,7 @@ describe Nanoc::CLI::ErrorHandler, stdio: true do
+@@ -80,7 +80,7 @@ describe Nanoc::CLI::ErrorHandler, stdio
        context 'no exit on error' do
          let(:exit_on_error) { false }
  
@@ -80,10 +80,10 @@ index b9a865d..63cd1bd 100644
            expect { subject }.not_to raise_error
          end
        end
-diff --git a/nanoc-core/spec/manifest_spec.rb b/nanoc-core/spec/manifest_spec.rb
-index 4be5711..9223165 100644
---- a/nanoc-core/spec/manifest_spec.rb
-+++ b/nanoc-core/spec/manifest_spec.rb
+Index: nanoc.git/nanoc-core/spec/manifest_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-core/spec/manifest_spec.rb
++++ nanoc.git/nanoc-core/spec/manifest_spec.rb
 @@ -1,7 +1,7 @@
  # frozen_string_literal: true
  
@@ -97,10 +97,10 @@ index 4be5711..9223165 100644
 +#    expect('nanoc-core').to have_a_valid_manifest
 +#  end
 +#end
-diff --git a/nanoc-core/spec/nanoc/core/feature_spec.rb b/nanoc-core/spec/nanoc/core/feature_spec.rb
-index c39d8e1..d04bedd 100644
---- a/nanoc-core/spec/nanoc/core/feature_spec.rb
-+++ b/nanoc-core/spec/nanoc/core/feature_spec.rb
+Index: nanoc.git/nanoc-core/spec/nanoc/core/feature_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-core/spec/nanoc/core/feature_spec.rb
++++ nanoc.git/nanoc-core/spec/nanoc/core/feature_spec.rb
 @@ -79,24 +79,24 @@ describe Nanoc::Core::Feature do
      end
    end
@@ -112,17 +112,6 @@ index c39d8e1..d04bedd 100644
 -      # in the current release.
 -      expect(described_class.all_outdated).to be_empty
 -    end
--
--    describe 'fake outdated features' do
--      before { described_class.define('abc', version: '4.2.x') }
--
--      after { described_class.undefine('abc') }
--
--      it 'detects outdated features' do
--        expect(described_class.all_outdated).to eq(['abc'])
--      end
--    end
--  end
 +  #describe '.all_outdated' do
 +  #  it 'refuses outdated features' do
 +  #    # If this spec fails, there are features marked as experimental in the previous minor or major
@@ -130,12 +119,20 @@ index c39d8e1..d04bedd 100644
 +  #    # in the current release.
 +  #    expect(described_class.all_outdated).to be_empty
 +  #  end
-+
+ 
+-    describe 'fake outdated features' do
+-      before { described_class.define('abc', version: '4.2.x') }
 +  #  describe 'fake outdated features' do
 +  #    before { described_class.define('abc', version: '4.2.x') }
-+
+ 
+-      after { described_class.undefine('abc') }
 +  #    after { described_class.undefine('abc') }
-+
+ 
+-      it 'detects outdated features' do
+-        expect(described_class.all_outdated).to eq(['abc'])
+-      end
+-    end
+-  end
 +  #    it 'detects outdated features' do
 +  #      expect(described_class.all_outdated).to eq(['abc'])
 +  #    end
@@ -144,11 +141,11 @@ index c39d8e1..d04bedd 100644
  
    describe '.define and .undefine' do
      let(:feature_name) { 'testing123' }
-diff --git a/nanoc-live/spec/gem_spec.rb b/nanoc-live/spec/gem_spec.rb
-index 8a4dd6d..9fe28f2 100644
---- a/nanoc-live/spec/gem_spec.rb
-+++ b/nanoc-live/spec/gem_spec.rb
-@@ -11,7 +11,7 @@ describe 'nanoc-live.gem', chdir: false, stdio: true do
+Index: nanoc.git/nanoc-live/spec/gem_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-live/spec/gem_spec.rb
++++ nanoc.git/nanoc-live/spec/gem_spec.rb
+@@ -11,7 +11,7 @@ describe 'nanoc-live.gem', chdir: false,
      Dir['*.gem'].each { |f| FileUtils.rm(f) }
    end
  
@@ -157,10 +154,10 @@ index 8a4dd6d..9fe28f2 100644
      expect { subject }
        .to change { Dir['*.gem'] }
        .from([])
-diff --git a/nanoc-live/spec/manifest_spec.rb b/nanoc-live/spec/manifest_spec.rb
-index b82bb83..268e73d 100644
---- a/nanoc-live/spec/manifest_spec.rb
-+++ b/nanoc-live/spec/manifest_spec.rb
+Index: nanoc.git/nanoc-live/spec/manifest_spec.rb
+===================================================================
+--- nanoc.git.orig/nanoc-live/spec/manifest_spec.rb
++++ nanoc.git/nanoc-live/spec/manifest_spec.rb
 @@ -1,7 +1,7 @@
  # frozen_string_literal: true
  
@@ -174,11 +171,11 @@ index b82bb83..268e73d 100644
 +#    expect('nanoc-live').to have_a_valid_manifest
 +#  end
 +#end
-diff --git a/nanoc/test/filters/test_erubi.rb b/nanoc/test/filters/test_erubi.rb
-index 91796dd..73e8b0e 100644
---- a/nanoc/test/filters/test_erubi.rb
-+++ b/nanoc/test/filters/test_erubi.rb
-@@ -23,7 +23,7 @@ class Nanoc::Filters::ErubiTest < Nanoc::TestCase
+Index: nanoc.git/nanoc/test/filters/test_erubi.rb
+===================================================================
+--- nanoc.git.orig/nanoc/test/filters/test_erubi.rb
++++ nanoc.git/nanoc/test/filters/test_erubi.rb
+@@ -23,7 +23,7 @@ class Nanoc::Filters::ErubiTest < Nanoc:
      assert_equal('I was hiding in a cheap motel.', result)
    end
  
diff --git a/guard-nanoc/guard-nanoc.gemspec b/guard-nanoc/guard-nanoc.gemspec
index 5e94202..d14e8d5 100644
--- a/guard-nanoc/guard-nanoc.gemspec
+++ b/guard-nanoc/guard-nanoc.gemspec
@@ -13,6 +13,8 @@ Gem::Specification.new do |s|
   s.author        = 'Denis Defreyne'
   s.email         = 'denis.defreyne@stoneship.org'
 
+  s.required_ruby_version = '>= 2.7'
+
   s.add_dependency 'guard', '~> 2.8'
   s.add_dependency 'guard-compat', '~> 1.0'
   s.add_dependency 'nanoc-cli', '~> 4.11', '>= 4.11.14'
diff --git a/guard-nanoc/lib/guard/nanoc/live_command.rb b/guard-nanoc/lib/guard/nanoc/live_command.rb
index 70f2dd7..bbc2ae5 100644
--- a/guard-nanoc/lib/guard/nanoc/live_command.rb
+++ b/guard-nanoc/lib/guard/nanoc/live_command.rb
@@ -9,8 +9,8 @@ description <<~EOS
 EOS
 
 required :H, :handler,       'specify the handler to use (webrick/puma/...)'
-required :o, :host,          'specify the host to listen on (default: 0.0.0.0)', default: '127.0.0.1'
-required :p, :port,          'specify the port to listen on (default: 3000)', transform: Nanoc::CLI::Transform::Port, default: 3000
+required :o, :host,          'specify the host to listen on', default: '127.0.0.1'
+required :p, :port,          'specify the port to listen on', transform: Nanoc::CLI::Transform::Port, default: 3000
 flag     :L, :'live-reload', 'reload on changes'
 
 module Guard
diff --git a/nanoc-checking/spec/nanoc/checking/checks/external_links_spec.rb b/nanoc-checking/spec/nanoc/checking/checks/external_links_spec.rb
index b3f90d5..4252114 100644
--- a/nanoc-checking/spec/nanoc/checking/checks/external_links_spec.rb
+++ b/nanoc-checking/spec/nanoc/checking/checks/external_links_spec.rb
@@ -123,16 +123,13 @@ describe Nanoc::Checking::Checks::ExternalLinks do
   context 'invalid URL component' do
     before do
       skip 'Known failure on Windows' if Nanoc::Core.on_windows?
+      File.write('output/hi.html', '<a href="mailto:lol">stuff</a>')
     end
 
     let(:check) do
       described_class.create(site)
     end
 
-    before do
-      File.write('output/hi.html', '<a href="mailto:lol">stuff</a>')
-    end
-
     it 'has issues' do
       check.run
       expect(check.issues.size).to eq(1)
diff --git a/nanoc-checking/spec/nanoc/checking/runner_spec.rb b/nanoc-checking/spec/nanoc/checking/runner_spec.rb
index fcb8cf0..806ce82 100644
--- a/nanoc-checking/spec/nanoc/checking/runner_spec.rb
+++ b/nanoc-checking/spec/nanoc/checking/runner_spec.rb
@@ -96,7 +96,7 @@ describe Nanoc::Checking::Runner, site: true do
           File.write('nanoc.yaml', "checking:\n  enabled_checks:\n    - elinks")
         end
 
-        it { is_expected.to match_array([:elinks]) }
+        it { is_expected.to contain_exactly(:elinks) }
       end
     end
 
@@ -114,7 +114,7 @@ describe Nanoc::Checking::Runner, site: true do
           File.write('nanoc.yaml', "checking:\n  enabled_checks:\n    - elinks")
         end
 
-        it { is_expected.to match_array([:elinks]) }
+        it { is_expected.to contain_exactly(:elinks) }
       end
     end
 
@@ -124,7 +124,7 @@ describe Nanoc::Checking::Runner, site: true do
       end
 
       context 'no deploy checks defined in config' do
-        it { is_expected.to match_array([:ilinks]) }
+        it { is_expected.to contain_exactly(:ilinks) }
       end
 
       context 'deploy checks defined in config' do
diff --git a/nanoc-cli/lib/nanoc/cli/commands/view.rb b/nanoc-cli/lib/nanoc/cli/commands/view.rb
index 50c315e..1d0bc18 100644
--- a/nanoc-cli/lib/nanoc/cli/commands/view.rb
+++ b/nanoc-cli/lib/nanoc/cli/commands/view.rb
@@ -9,8 +9,8 @@ description <<~EOS
 EOS
 
 required :H, :handler, 'specify the handler to use (webrick/puma/...)'
-required :o, :host,    'specify the host to listen on (default: 127.0.0.1)', default: '127.0.0.1'
-required :p, :port,    'specify the port to listen on (default: 3000)', transform: Nanoc::CLI::Transform::Port, default: 3000
+required :o, :host,    'specify the host to listen on', default: '127.0.0.1'
+required :p, :port,    'specify the port to listen on', transform: Nanoc::CLI::Transform::Port, default: 3000
 flag :L, :'live-reload', 'reload on changes'
 no_params
 
diff --git a/nanoc-cli/lib/nanoc/cli/compile_listeners/timing_recorder.rb b/nanoc-cli/lib/nanoc/cli/compile_listeners/timing_recorder.rb
index 4012020..6201cf2 100644
--- a/nanoc-cli/lib/nanoc/cli/compile_listeners/timing_recorder.rb
+++ b/nanoc-cli/lib/nanoc/cli/compile_listeners/timing_recorder.rb
@@ -168,7 +168,7 @@ module Nanoc::CLI::CompileListeners
     end
 
     def print_table(rows)
-      puts DDMetrics::Table.new(rows).to_s
+      puts DDMetrics::Table.new(rows)
     end
   end
 end
diff --git a/nanoc-cli/lib/nanoc/cli/error_handler.rb b/nanoc-cli/lib/nanoc/cli/error_handler.rb
index dc540d7..e1bef6f 100644
--- a/nanoc-cli/lib/nanoc/cli/error_handler.rb
+++ b/nanoc-cli/lib/nanoc/cli/error_handler.rb
@@ -115,6 +115,7 @@ module Nanoc::CLI
       stream.puts 'Captain! We’ve been hit!'
 
       write_error_message(stream, error)
+      write_error_detail(stream, error)
       write_item_rep(stream, error)
       write_stack_trace(stream, error)
 
@@ -133,6 +134,7 @@ module Nanoc::CLI
       stream.puts "Crashlog created at #{Time.now}"
 
       write_error_message(stream, error, verbose: true)
+      write_error_detail(stream, error)
       write_item_rep(stream, error, verbose: true)
       write_stack_trace(stream, error, verbose: true)
       write_version_information(stream, verbose: true)
@@ -292,6 +294,15 @@ module Nanoc::CLI
       end
     end
 
+    def write_error_detail(stream, error)
+      error = unwrap_error(error)
+
+      if error.respond_to?(:full_message)
+        stream.puts
+        stream.puts error.full_message
+      end
+    end
+
     def write_item_rep(stream, error, verbose: false)
       return unless error.is_a?(Nanoc::Core::Errors::CompilationError)
 
diff --git a/nanoc-cli/lib/nanoc/cli/version.rb b/nanoc-cli/lib/nanoc/cli/version.rb
index f99754f..597a083 100644
--- a/nanoc-cli/lib/nanoc/cli/version.rb
+++ b/nanoc-cli/lib/nanoc/cli/version.rb
@@ -2,6 +2,6 @@
 
 module Nanoc
   module CLI
-    VERSION = '4.12.15'
+    VERSION = '4.12.16'
   end
 end
diff --git a/nanoc-cli/spec/nanoc/cli/compile_listeners/file_action_printer_spec.rb b/nanoc-cli/spec/nanoc/cli/compile_listeners/file_action_printer_spec.rb
index f66059e..860e3e7 100644
--- a/nanoc-cli/spec/nanoc/cli/compile_listeners/file_action_printer_spec.rb
+++ b/nanoc-cli/spec/nanoc/cli/compile_listeners/file_action_printer_spec.rb
@@ -16,9 +16,10 @@ describe Nanoc::CLI::CompileListeners::FileActionPrinter, stdio: true do
 
   before { Timecop.freeze(Time.local(2008, 1, 2, 14, 5, 0)) }
 
-  after { Timecop.return }
-
-  after { listener.stop_safely }
+  after do
+    Timecop.return
+    listener.stop_safely
+  end
 
   it 'records from compilation_started to rep_write_ended' do
     listener.start_safely
@@ -72,9 +73,10 @@ describe Nanoc::CLI::CompileListeners::FileActionPrinter, stdio: true do
   end
 
   context 'log level = high' do
-    before { listener.start_safely }
-
-    before { Nanoc::CLI::Logger.instance.level = :high }
+    before do
+      listener.start_safely
+      Nanoc::CLI::Logger.instance.level = :high
+    end
 
     it 'does not print skipped (uncompiled) reps' do
       expect { listener.stop_safely }
@@ -100,9 +102,10 @@ describe Nanoc::CLI::CompileListeners::FileActionPrinter, stdio: true do
   end
 
   context 'log level = low' do
-    before { listener.start_safely }
-
-    before { Nanoc::CLI::Logger.instance.level = :low }
+    before do
+      listener.start_safely
+      Nanoc::CLI::Logger.instance.level = :low
+    end
 
     it 'prints skipped (uncompiled) reps' do
       expect { listener.stop_safely }
diff --git a/nanoc-cli/spec/nanoc/cli/compile_listeners/timing_recorder_spec.rb b/nanoc-cli/spec/nanoc/cli/compile_listeners/timing_recorder_spec.rb
index d56620f..166670c 100644
--- a/nanoc-cli/spec/nanoc/cli/compile_listeners/timing_recorder_spec.rb
+++ b/nanoc-cli/spec/nanoc/cli/compile_listeners/timing_recorder_spec.rb
@@ -19,15 +19,16 @@ describe Nanoc::CLI::CompileListeners::TimingRecorder, stdio: true do
     end
   end
 
-  before { Timecop.freeze(Time.local(2008, 1, 2, 14, 5, 0)) }
-
-  after { Timecop.return }
-
-  before { Nanoc::CLI.verbosity = 2 }
-
-  before { listener.start_safely }
+  before do
+    Timecop.freeze(Time.local(2008, 1, 2, 14, 5, 0))
+    Nanoc::CLI.verbosity = 2
+    listener.start_safely
+  end
 
-  after { listener.stop_safely }
+  after do
+    Timecop.return
+    listener.stop_safely
+  end
 
   it 'prints filters table' do
     Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
diff --git a/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb b/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
index bc9db58..da40a2e 100644
--- a/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
+++ b/nanoc-cli/spec/nanoc/cli/error_handler_spec.rb
@@ -118,6 +118,34 @@ describe Nanoc::CLI::ErrorHandler, stdio: true do
         end
       end
 
+      context 'when error implements #full_message', stdio: true do
+        let(:klass) do
+          Class.new(StandardError) do
+            def self.to_s
+              'SubclassOfStandardError'
+            end
+
+            def full_message
+              "okay so what I mean is that #{message}"
+            end
+          end
+        end
+
+        let(:error) do
+          raise klass.new('it is broken')
+        rescue => e
+          return e
+        end
+
+        it 'prints error message followed by error detail' do
+          subject
+
+          expect($stderr.string).to match(
+            %r{SubclassOfStandardError: it is broken.*okay so what I mean is that it is broken}m,
+          )
+        end
+      end
+
       context 'non-trivial error' do
         # …
       end
diff --git a/nanoc-core/lib/nanoc/core.rb b/nanoc-core/lib/nanoc/core.rb
index 6cafa0b..0576cb2 100644
--- a/nanoc-core/lib/nanoc/core.rb
+++ b/nanoc-core/lib/nanoc/core.rb
@@ -14,7 +14,7 @@ require 'concurrent-ruby'
 require 'json_schema'
 require 'ddmetrics'
 require 'ddplugin'
-require 'hamster'
+require 'immutable'
 require 'memo_wise'
 require 'slow_enumerator_tools'
 require 'tty-platform'
diff --git a/nanoc-core/lib/nanoc/core/checksummer.rb b/nanoc-core/lib/nanoc/core/checksummer.rb
index 097836b..6cc9040 100644
--- a/nanoc-core/lib/nanoc/core/checksummer.rb
+++ b/nanoc-core/lib/nanoc/core/checksummer.rb
@@ -61,7 +61,7 @@ module Nanoc
 
         private
 
-        def update(obj, digest, visited = Hamster::Set.new)
+        def update(obj, digest, visited = Immutable::Set.new)
           digest.update(obj.class.to_s)
 
           if visited.include?(obj)
@@ -276,11 +276,15 @@ module Nanoc
 
       class RescueUpdateBehavior < UpdateBehavior
         def self.update(obj, digest)
+          # rubocop:disable Style/ClassEqualityComparison
+          # This Rubocop rule is disabled because the class
+          # itself might not be loaded (yet).
           if obj.class.to_s == 'Sass::Importers::Filesystem'
             digest.update('root=')
             digest.update(obj.root)
             return
           end
+          # rubocop:enable Style/ClassEqualityComparison
 
           data =
             begin
diff --git a/nanoc-core/lib/nanoc/core/identifiable_collection.rb b/nanoc-core/lib/nanoc/core/identifiable_collection.rb
index 295ab22..8bd873e 100644
--- a/nanoc-core/lib/nanoc/core/identifiable_collection.rb
+++ b/nanoc-core/lib/nanoc/core/identifiable_collection.rb
@@ -20,7 +20,7 @@ module Nanoc
       # contract C::Or[Hash, C::Named['Nanoc::Core::Configuration']], C::IterOf[C::RespondTo[:identifier]], C::Maybe[String] => C::Any
       def initialize_basic(config, objects = [], name = nil)
         @config = config
-        @objects = Hamster::Vector.new(objects)
+        @objects = Immutable::Vector.new(objects)
         @name = name
       end
 
diff --git a/nanoc-core/lib/nanoc/core/outdatedness_checker.rb b/nanoc-core/lib/nanoc/core/outdatedness_checker.rb
index 6c98109..713a8a6 100644
--- a/nanoc-core/lib/nanoc/core/outdatedness_checker.rb
+++ b/nanoc-core/lib/nanoc/core/outdatedness_checker.rb
@@ -80,8 +80,8 @@ module Nanoc
         )
       end
 
-      contract C_OBJ, Hamster::Set => C::Bool
-      def outdated_due_to_dependencies?(obj, processed = Hamster::Set.new)
+      contract C_OBJ, Immutable::Set => C::Bool
+      def outdated_due_to_dependencies?(obj, processed = Immutable::Set.new)
         # Convert from rep to item if necessary
         obj = obj.item if obj.is_a?(Nanoc::Core::ItemRep)
 
@@ -168,7 +168,7 @@ module Nanoc
         # outdatedness.
         matching_objects.any? do |obj|
           status = basic_outdatedness_statuses.fetch(obj)
-          status.reasons.any? { |r| Nanoc::Core::OutdatednessReasons::DocumentAdded == r }
+          status.reasons.any? { |r| r == Nanoc::Core::OutdatednessReasons::DocumentAdded }
         end
       end
 
diff --git a/nanoc-core/lib/nanoc/core/version.rb b/nanoc-core/lib/nanoc/core/version.rb
index 76672c1..1e75c2f 100644
--- a/nanoc-core/lib/nanoc/core/version.rb
+++ b/nanoc-core/lib/nanoc/core/version.rb
@@ -2,6 +2,6 @@
 
 module Nanoc
   module Core
-    VERSION = '4.12.15'
+    VERSION = '4.12.16'
   end
 end
diff --git a/nanoc-core/nanoc-core.gemspec b/nanoc-core/nanoc-core.gemspec
index 8ac4714..b890416 100644
--- a/nanoc-core/nanoc-core.gemspec
+++ b/nanoc-core/nanoc-core.gemspec
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
   s.add_runtime_dependency('concurrent-ruby', '~> 1.1')
   s.add_runtime_dependency('ddmetrics', '~> 1.0')
   s.add_runtime_dependency('ddplugin', '~> 1.0')
-  s.add_runtime_dependency('hamster', '~> 3.0')
+  s.add_runtime_dependency('immutable-ruby', '~> 0.1')
   s.add_runtime_dependency('json_schema', '~> 0.19')
   s.add_runtime_dependency('memo_wise', '~> 1.5')
   s.add_runtime_dependency('psych', '~> 4.0')
diff --git a/nanoc-core/spec/nanoc/core/compilation_stages/compile_reps_spec.rb b/nanoc-core/spec/nanoc/core/compilation_stages/compile_reps_spec.rb
index 9e87d73..78087de 100644
--- a/nanoc-core/spec/nanoc/core/compilation_stages/compile_reps_spec.rb
+++ b/nanoc-core/spec/nanoc/core/compilation_stages/compile_reps_spec.rb
@@ -124,9 +124,9 @@ describe Nanoc::Core::CompilationStages::CompileReps do
     end
 
     context 'rep in outdatedness store' do
-      before { outdatedness_store.add(rep) }
-
       before do
+        outdatedness_store.add(rep)
+
         # Needed for consistency
         compiled_content_cache[other_rep] = { last: Nanoc::Core::TextualContent.new('asdf') }
       end
diff --git a/nanoc-core/spec/nanoc/core/compilation_stages/preprocess_spec.rb b/nanoc-core/spec/nanoc/core/compilation_stages/preprocess_spec.rb
index c13e7c7..63ac297 100644
--- a/nanoc-core/spec/nanoc/core/compilation_stages/preprocess_spec.rb
+++ b/nanoc-core/spec/nanoc/core/compilation_stages/preprocess_spec.rb
@@ -109,7 +109,7 @@ describe Nanoc::Core::CompilationStages::Preprocess do
         expect { subject }
           .to change { checksum_store.objects.to_a }
           .from([])
-          .to(match_array([new_item, new_layout, config]))
+          .to(contain_exactly(new_item, new_layout, config))
       end
     end
   end
diff --git a/nanoc-core/spec/nanoc/core/config_loader_spec.rb b/nanoc-core/spec/nanoc/core/config_loader_spec.rb
index 835f2e8..9166514 100644
--- a/nanoc-core/spec/nanoc/core/config_loader_spec.rb
+++ b/nanoc-core/spec/nanoc/core/config_loader_spec.rb
@@ -71,9 +71,6 @@ describe Nanoc::Core::ConfigLoader do
 
       before do
         File.write('nanoc.yaml', YAML.dump(config))
-      end
-
-      before do
         expect(ENV).to receive(:fetch).with('NANOC_ENV', 'default').and_return(active_env_name)
       end
 
diff --git a/nanoc-core/spec/nanoc/core/dependency_store_spec.rb b/nanoc-core/spec/nanoc/core/dependency_store_spec.rb
index e477822..7c727e1 100644
--- a/nanoc-core/spec/nanoc/core/dependency_store_spec.rb
+++ b/nanoc-core/spec/nanoc/core/dependency_store_spec.rb
@@ -262,7 +262,7 @@ describe Nanoc::Core::DependencyStore do
       end
 
       it 'has one new item' do
-        expect(reloaded_store.new_items).to match_array([item_d])
+        expect(reloaded_store.new_items).to contain_exactly(item_d)
       end
 
       it 'has no new layouts' do
@@ -452,7 +452,7 @@ describe Nanoc::Core::DependencyStore do
         deps = store.dependencies_causing_outdatedness_of(source_obj)
 
         expect(deps.first.props.attributes?).to be(true)
-        expect(deps.first.props.attributes).to match_array([:giraffe])
+        expect(deps.first.props.attributes).to contain_exactly(:giraffe)
         expect(deps.first.props.compiled_content?).to be(false)
       end
 
@@ -509,7 +509,7 @@ describe Nanoc::Core::DependencyStore do
     it 'removes dependencies from item_b' do
       expect { subject }
         .to change { store.objects_causing_outdatedness_of(item_b) }
-        .from(match_array([item_a, item_c]))
+        .from(contain_exactly(item_a, item_c))
         .to(be_empty)
     end
 
diff --git a/nanoc-core/spec/nanoc/core/directed_graph_spec.rb b/nanoc-core/spec/nanoc/core/directed_graph_spec.rb
index d36d8f9..bb608d0 100644
--- a/nanoc-core/spec/nanoc/core/directed_graph_spec.rb
+++ b/nanoc-core/spec/nanoc/core/directed_graph_spec.rb
@@ -29,7 +29,7 @@ describe Nanoc::Core::DirectedGraph do
         graph.add_edge('1', '3')
       end
 
-      it { is_expected.to match_array([[0, 1, nil], [0, 2, nil]]) }
+      it { is_expected.to contain_exactly([0, 1, nil], [0, 2, nil]) }
     end
 
     context 'graph with edges from new vertices' do
@@ -38,7 +38,7 @@ describe Nanoc::Core::DirectedGraph do
         graph.add_edge('1', '3')
       end
 
-      it { is_expected.to match_array([[0, 1, nil], [0, 2, nil]]) }
+      it { is_expected.to contain_exactly([0, 1, nil], [0, 2, nil]) }
     end
 
     context 'graph with edge props' do
@@ -47,7 +47,7 @@ describe Nanoc::Core::DirectedGraph do
         graph.add_edge('1', '3', props: { name: 'Cooper' })
       end
 
-      it { is_expected.to match_array([[0, 1, { name: 'Mr. C' }], [0, 2, { name: 'Cooper' }]]) }
+      it { is_expected.to contain_exactly([0, 1, { name: 'Mr. C' }], [0, 2, { name: 'Cooper' }]) }
     end
   end
 
@@ -125,7 +125,7 @@ describe Nanoc::Core::DirectedGraph do
     context 'one edge to' do
       before { graph.add_edge('1', '2') }
 
-      it { is_expected.to match_array(['1']) }
+      it { is_expected.to contain_exactly('1') }
       it { is_expected.to be_a(Set) }
     end
 
@@ -172,7 +172,7 @@ describe Nanoc::Core::DirectedGraph do
       end
 
       context 'no indirect predecessors' do
-        it { is_expected.to match_array(['1']) }
+        it { is_expected.to contain_exactly('1') }
       end
 
       context 'indirect predecessors' do
diff --git a/nanoc-core/spec/nanoc/core/identifiable_collection_spec.rb b/nanoc-core/spec/nanoc/core/identifiable_collection_spec.rb
index d44a2e5..e76f79e 100644
--- a/nanoc-core/spec/nanoc/core/identifiable_collection_spec.rb
+++ b/nanoc-core/spec/nanoc/core/identifiable_collection_spec.rb
@@ -257,7 +257,7 @@ describe Nanoc::Core::IdentifiableCollection do
       it 'loops' do
         res = []
         identifiable_collection.each { |i| res << i.identifier.to_s }
-        expect(res).to match_array(['/foo', '/bar'])
+        expect(res).to contain_exactly('/foo', '/bar')
       end
     end
 
@@ -271,7 +271,7 @@ describe Nanoc::Core::IdentifiableCollection do
 
       it 'loops' do
         res = identifiable_collection.map { |i| i.identifier.to_s }
-        expect(res).to match_array(['/foo', '/bar'])
+        expect(res).to contain_exactly('/foo', '/bar')
       end
     end
   end
diff --git a/nanoc-core/spec/nanoc/core/outdatedness_checker_spec.rb b/nanoc-core/spec/nanoc/core/outdatedness_checker_spec.rb
index fb1e1d3..3db5139 100644
--- a/nanoc-core/spec/nanoc/core/outdatedness_checker_spec.rb
+++ b/nanoc-core/spec/nanoc/core/outdatedness_checker_spec.rb
@@ -201,9 +201,7 @@ describe Nanoc::Core::OutdatednessChecker do
     let(:item_home_rep_after)      { Nanoc::Core::ItemRep.new(item_home_after, :default) }
 
     it 'marks home item as outdated' do
-      expect(outdatedness_checker.outdatedness_reasons_for(item_home_after)).to match_array([
-        Nanoc::Core::OutdatednessReasons::ContentModified,
-      ])
+      expect(outdatedness_checker.outdatedness_reasons_for(item_home_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::ContentModified)
     end
 
     it 'marks other items as NOT outdated' do
@@ -232,15 +230,11 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::ContentModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::ContentModified)
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -264,9 +258,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::ContentModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::ContentModified)
       end
 
       it 'marks other items as NOT outdated' do
@@ -291,15 +283,11 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::ContentModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::ContentModified)
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -323,9 +311,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::ContentModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::ContentModified)
       end
 
       it 'marks other items as NOT outdated' do
@@ -355,9 +341,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::AttributesModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::AttributesModified)
       end
 
       it 'marks other items as NOT outdated' do
@@ -382,15 +366,11 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::AttributesModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::AttributesModified)
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -414,15 +394,11 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::AttributesModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::AttributesModified)
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -446,9 +422,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::AttributesModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::AttributesModified)
       end
 
       it 'marks other items as NOT outdated' do
@@ -473,15 +447,11 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::AttributesModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::AttributesModified)
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -505,9 +475,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::AttributesModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::AttributesModified)
       end
 
       it 'marks other items as NOT outdated' do
@@ -534,9 +502,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -620,15 +586,11 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks home item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_home_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_home_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -674,9 +636,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -699,9 +659,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -759,9 +717,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::RulesModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::RulesModified)
       end
 
       it 'marks other items as NOT outdated' do
@@ -786,9 +742,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::RulesModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::RulesModified)
       end
 
       it 'marks other items as NOT outdated' do
@@ -813,15 +767,11 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::RulesModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::RulesModified)
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -845,9 +795,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks article item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::RulesModified,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::RulesModified)
       end
 
       it 'marks articles item as outdated' do
@@ -855,9 +803,7 @@ describe Nanoc::Core::OutdatednessChecker do
         # articles item should not be considered as outdated. This is because
         # the `RulesModified` outdatedness reason has the property `path: true`.
 
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -875,21 +821,11 @@ describe Nanoc::Core::OutdatednessChecker do
     let(:code_snippet_b_after) { Nanoc::Core::CodeSnippet.new('bbb UPDATED', 'lib/b.rb') }
 
     it 'marks all items as outdated' do
-      expect(outdatedness_checker.outdatedness_reasons_for(item_home_after)).to match_array([
-        Nanoc::Core::OutdatednessReasons::CodeSnippetsModified,
-      ])
-      expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-        Nanoc::Core::OutdatednessReasons::CodeSnippetsModified,
-      ])
-      expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-        Nanoc::Core::OutdatednessReasons::CodeSnippetsModified,
-      ])
-      expect(outdatedness_checker.outdatedness_reasons_for(item_article_b_after)).to match_array([
-        Nanoc::Core::OutdatednessReasons::CodeSnippetsModified,
-      ])
-      expect(outdatedness_checker.outdatedness_reasons_for(item_article_c_after)).to match_array([
-        Nanoc::Core::OutdatednessReasons::CodeSnippetsModified,
-      ])
+      expect(outdatedness_checker.outdatedness_reasons_for(item_home_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::CodeSnippetsModified)
+      expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::CodeSnippetsModified)
+      expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::CodeSnippetsModified)
+      expect(outdatedness_checker.outdatedness_reasons_for(item_article_b_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::CodeSnippetsModified)
+      expect(outdatedness_checker.outdatedness_reasons_for(item_article_c_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::CodeSnippetsModified)
     end
   end
 
@@ -904,9 +840,7 @@ describe Nanoc::Core::OutdatednessChecker do
     end
 
     it 'marks article item as outdated' do
-      expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-        Nanoc::Core::OutdatednessReasons::UsesAlwaysOutdatedFilter,
-      ])
+      expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::UsesAlwaysOutdatedFilter)
     end
 
     it 'marks other items as NOT outdated' do
@@ -925,9 +859,7 @@ describe Nanoc::Core::OutdatednessChecker do
     end
 
     it 'marks article item as outdated' do
-      expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to match_array([
-        Nanoc::Core::OutdatednessReasons::NotWritten,
-      ])
+      expect(outdatedness_checker.outdatedness_reasons_for(item_article_a_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::NotWritten)
     end
 
     it 'marks other items as NOT outdated' do
@@ -963,9 +895,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -988,9 +918,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -1098,9 +1026,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks new item as outdated' do
@@ -1173,9 +1099,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks new item as outdated' do
@@ -1213,9 +1137,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks new item as outdated' do
@@ -1253,9 +1175,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks new item as outdated' do
@@ -1332,9 +1252,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -1389,9 +1307,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -1420,9 +1336,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
@@ -1451,9 +1365,7 @@ describe Nanoc::Core::OutdatednessChecker do
       end
 
       it 'marks articles item as outdated' do
-        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to match_array([
-          Nanoc::Core::OutdatednessReasons::DependenciesOutdated,
-        ])
+        expect(outdatedness_checker.outdatedness_reasons_for(item_articles_after)).to contain_exactly(Nanoc::Core::OutdatednessReasons::DependenciesOutdated)
       end
 
       it 'marks other items as NOT outdated' do
diff --git a/nanoc-core/spec/nanoc/core/pruner_spec.rb b/nanoc-core/spec/nanoc/core/pruner_spec.rb
index e5deb62..c0a4a28 100644
--- a/nanoc-core/spec/nanoc/core/pruner_spec.rb
+++ b/nanoc-core/spec/nanoc/core/pruner_spec.rb
@@ -401,9 +401,6 @@ describe Nanoc::Core::Pruner, stdio: true do
       before do
         FileUtils.mv('output', 'output-real')
         File.symlink('output-real', 'output')
-      end
-
-      before do
         if Nanoc::Core.on_windows?
           skip 'Symlinks to output dirs are currently not supported on Windows.'
         end
diff --git a/nanoc-core/spec/nanoc/core/site_loader_spec.rb b/nanoc-core/spec/nanoc/core/site_loader_spec.rb
index 7268fb6..84c4261 100644
--- a/nanoc-core/spec/nanoc/core/site_loader_spec.rb
+++ b/nanoc-core/spec/nanoc/core/site_loader_spec.rb
@@ -117,9 +117,7 @@ describe Nanoc::Core::SiteLoader do
             staging:
               animal: giraffe
         EOS
-      end
 
-      before do
         expect(ENV).to receive(:fetch).with('NANOC_ENV', 'default').and_return('staging')
       end
 
diff --git a/nanoc-dart-sass/NEWS.md b/nanoc-dart-sass/NEWS.md
index fad46c4..cdbbe48 100644
--- a/nanoc-dart-sass/NEWS.md
+++ b/nanoc-dart-sass/NEWS.md
@@ -1,5 +1,9 @@
 # nanoc-dart-sass news
 
+## 1.0.1 (2023-06-15)
+
+- Added support for importing relative paths (#1646, #1653)
+
 ## 1.0.0 (2022-12-17)
 
 Initial release.
diff --git a/nanoc-dart-sass/lib/nanoc/dart_sass/filter.rb b/nanoc-dart-sass/lib/nanoc/dart_sass/filter.rb
index 64e68b3..d428269 100644
--- a/nanoc-dart-sass/lib/nanoc/dart_sass/filter.rb
+++ b/nanoc-dart-sass/lib/nanoc/dart_sass/filter.rb
@@ -18,35 +18,65 @@ module Nanoc
 
         result = Sass.compile_string(
           content,
-          importer: NanocImporter.new(@items),
+          importer: NanocImporter.new(@items, item),
           **params,
           syntax: syntax,
         )
         result.css
-      rescue StandardError => e # rubocop:disable Lint/UselessRescue
-        # TODO: use full_message for syntax errors
-        raise e
       end
 
       class NanocImporter
-        def initialize(items)
+        def initialize(items, source_item)
           @items = items
+          @source_item = source_item
         end
 
         def canonicalize(url, **)
-          "nanoc:#{@items[url.sub(/\Ananoc:/, '')].identifier}"
+          # Construct proper URL with `nanoc:` prefix if needed
+          if url.start_with?('nanoc:')
+            url
+          else
+            "nanoc:#{url}"
+          end
         end
 
         def load(url)
-          item = @items[url.sub(/\Ananoc:/, '')]
+          item = find_item_for_url(url)
+
           {
             contents: item.raw_content,
             syntax: Util.syntax_from_ext(item.identifier.ext),
           }
         end
-      end
 
-      private_constant :NanocImporter
+        private
+
+        def find_item_for_url(url)
+          pat = url.sub(/\Ananoc:/, '')
+
+          # If URL has no extension, add `.*` at the end
+          if pat.match?(%r{(/|^)[^.]+$})
+            pat += '.*'
+          end
+
+          # Convert to absolute pattern
+          pat =
+            if pat.start_with?('/')
+              pat
+            else
+              dirname = File.dirname(@source_item.identifier.to_s)
+              File.expand_path(pat, dirname)
+            end
+
+          item = @items[pat]
+
+          unless item
+            raise "Could not find an item matching pattern `#{pat}`"
+          end
+
+          item
+        end
+      end
 
       module Util
         module_function
diff --git a/nanoc-dart-sass/lib/nanoc/dart_sass/version.rb b/nanoc-dart-sass/lib/nanoc/dart_sass/version.rb
index c34fa5f..806ab1c 100644
--- a/nanoc-dart-sass/lib/nanoc/dart_sass/version.rb
+++ b/nanoc-dart-sass/lib/nanoc/dart_sass/version.rb
@@ -2,6 +2,6 @@
 
 module Nanoc
   module DartSass
-    VERSION = '1.0.0'
+    VERSION = '1.0.1'
   end
 end
diff --git a/nanoc-dart-sass/spec/nanoc/dart_sass/filter/nanoc_importer_spec.rb b/nanoc-dart-sass/spec/nanoc/dart_sass/filter/nanoc_importer_spec.rb
new file mode 100644
index 0000000..b15da14
--- /dev/null
+++ b/nanoc-dart-sass/spec/nanoc/dart_sass/filter/nanoc_importer_spec.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+describe Nanoc::DartSass::Filter::NanocImporter do
+  let(:importer) { described_class.new(items_view, source_item) }
+
+  let(:items_view) { Nanoc::Core::ItemCollectionWithoutRepsView.new(items, view_context) }
+  let(:config) { Nanoc::Core::Configuration.new(dir: Dir.getwd).with_defaults }
+  let(:base_item) { Nanoc::Core::Item.new('base', {}, '/base.md') }
+  let(:dependency_store) { Nanoc::Core::DependencyStore.new(items, layouts, config) }
+  let(:dependency_tracker) { Nanoc::Core::DependencyTracker.new(dependency_store) }
+
+  let(:items) { Nanoc::Core::ItemCollection.new(config, items_array) }
+  let(:layouts) { Nanoc::Core::LayoutCollection.new(config) }
+  let(:reps) { Nanoc::Core::ItemRepRepo.new }
+
+  let(:view_context) do
+    Nanoc::Core::ViewContextForCompilation.new(
+      reps: Nanoc::Core::ItemRepRepo.new,
+      items: items,
+      dependency_tracker: dependency_tracker,
+      compilation_context: compilation_context,
+      compiled_content_store: compiled_content_store,
+    )
+  end
+
+  let(:compilation_context) do
+    Nanoc::Core::CompilationContext.new(
+      action_provider: action_provider,
+      reps: reps,
+      site: site,
+      compiled_content_cache: compiled_content_cache,
+      compiled_content_store: compiled_content_store,
+    )
+  end
+
+  let(:compiled_content_store) { Nanoc::Core::CompiledContentStore.new }
+  let(:compiled_content_cache) { Nanoc::Core::CompiledContentCache.new(config: config) }
+
+  let(:site) do
+    Nanoc::Core::Site.new(
+      config: config,
+      code_snippets: [],
+      data_source: Nanoc::Core::InMemoryDataSource.new(items, layouts),
+    )
+  end
+
+  let(:action_provider) do
+    Class.new(Nanoc::Core::ActionProvider) do
+      def self.for(_context)
+        raise NotImplementedError
+      end
+    end.new
+  end
+
+  let(:screen_item) { Nanoc::Core::Item.new('screen content here', {}, '/assets/style/screen.scss') }
+  let(:colors_item) { Nanoc::Core::Item.new('colors content here', {}, '/assets/style/colors.scss') }
+  let(:fonts_item) { Nanoc::Core::Item.new('fonts content here', {}, '/assets/fonts.scss') }
+  let(:source_item) { screen_item }
+
+  let(:items_array) do
+    [
+      screen_item,
+      colors_item,
+      fonts_item,
+    ]
+  end
+
+  describe '#canonicalize' do
+    subject { importer.canonicalize(url) }
+
+    context 'when given a URL with nanoc: prefix' do
+      let(:url) { 'nanoc:foo' }
+
+      it { is_expected.to eq('nanoc:foo') }
+    end
+
+    context 'when given a URL without nanoc: prefix' do
+      let(:url) { 'foo' }
+
+      it { is_expected.to eq('nanoc:foo') }
+    end
+  end
+
+  describe '#load' do
+    subject { importer.load(url) }
+
+    context 'when importing absolute, full path' do
+      let(:url) { '/assets/style/colors.scss' }
+
+      it { is_expected.to eq({ contents: 'colors content here', syntax: :scss }) }
+    end
+
+    context 'when importing absolute path without extension' do
+      let(:url) { '/assets/style/colors' }
+
+      it { is_expected.to eq({ contents: 'colors content here', syntax: :scss }) }
+    end
+
+    context 'when importing relative path with dot with extension' do
+      let(:url) { './colors.scss' }
+
+      it { is_expected.to eq({ contents: 'colors content here', syntax: :scss }) }
+    end
+
+    context 'when importing absolute path with dot without extension' do
+      let(:url) { './colors' }
+
+      it { is_expected.to eq({ contents: 'colors content here', syntax: :scss }) }
+    end
+
+    context 'when importing relative path without dot with extension' do
+      let(:url) { 'colors.scss' }
+
+      it { is_expected.to eq({ contents: 'colors content here', syntax: :scss }) }
+    end
+
+    context 'when importing absolute path without dot without extension' do
+      let(:url) { 'colors' }
+
+      it { is_expected.to eq({ contents: 'colors content here', syntax: :scss }) }
+    end
+  end
+end
diff --git a/nanoc-dart-sass/spec/nanoc/dart_sass/filter_spec.rb b/nanoc-dart-sass/spec/nanoc/dart_sass/filter_spec.rb
index e94c187..c14315a 100644
--- a/nanoc-dart-sass/spec/nanoc/dart_sass/filter_spec.rb
+++ b/nanoc-dart-sass/spec/nanoc/dart_sass/filter_spec.rb
@@ -57,7 +57,7 @@ describe Nanoc::DartSass::Filter, helper: true do
     expect(res.strip).to match(/color: #333/m)
   end
 
-  context 'when one item depends on another' do
+  context 'when one item depends on another with absolute path' do
     before do
       # Create item
       ctx.create_item('stuff', {}, '/foo.scss')
@@ -94,4 +94,57 @@ describe Nanoc::DartSass::Filter, helper: true do
         .onto([instance_of(Nanoc::Core::ItemCollection), ctx.items['/defs.scss']])
     end
   end
+
+  context 'when one item depends on another with relative path' do
+    before do
+      # Create item
+      ctx.create_item('stuff', {}, '/assets/style/foo.scss')
+      ctx.create_rep(ctx.items['/assets/style/foo.scss'], '/assets/foo.css')
+      ctx.item = ctx.items['/assets/style/foo.scss']
+
+      # Create other items
+      ctx.create_item('$fg-color: #900;', {}, '/assets/style/defs1.scss')
+      ctx.create_rep(ctx.items['/assets/style/defs1.scss'], '/assets/defs1.css')
+      ctx.create_item('$bg-color: #dff;', {}, '/assets/style/defs2.scss')
+      ctx.create_rep(ctx.items['/assets/style/defs2.scss'], '/assets/defs2.css')
+      ctx.create_item('$hl: #f00;', {}, '/assets/style/defs3.scss')
+      ctx.create_rep(ctx.items['/assets/style/defs3.scss'], '/assets/defs3.css')
+    end
+
+    let(:content) do
+      <<~SCSS
+        @import './defs1.*';
+        @import 'defs2.*';
+        @import 'defs3';
+
+        body {
+          color: $fg-color;
+          background: $bg-color;
+        }
+      SCSS
+    end
+
+    it 'supports reading from dependencies' do
+      filter = described_class.new(ctx.assigns)
+
+      res = filter.run(content)
+      expect(res.strip).to match(/color: #900/m)
+      expect(res.strip).to match(/background: #dff/m)
+    end
+
+    it 'creates Nanoc dependencies' do
+      filter = described_class.new(ctx.assigns)
+
+      expect { filter.run(content) }
+        .to create_dependency_from(ctx.items['/assets/style/foo.scss'])
+        .onto(
+          [
+            instance_of(Nanoc::Core::ItemCollection),
+            ctx.items['/assets/style/defs1.scss'],
+            ctx.items['/assets/style/defs2.scss'],
+            ctx.items['/assets/style/defs3.scss'],
+          ],
+        )
+    end
+  end
 end
diff --git a/nanoc-live/lib/nanoc/live/commands/live.rb b/nanoc-live/lib/nanoc/live/commands/live.rb
index a9d3e65..3571519 100644
--- a/nanoc-live/lib/nanoc/live/commands/live.rb
+++ b/nanoc-live/lib/nanoc/live/commands/live.rb
@@ -9,8 +9,8 @@ description <<~EOS
 EOS
 
 required :H, :handler, 'specify the handler to use (webrick/puma/...)'
-required :o, :host,    'specify the host to listen on (default: 127.0.0.1)', default: '127.0.0.1'
-required :p, :port,    'specify the port to listen on (default: 3000)', transform: Nanoc::CLI::Transform::Port, default: 3000
+required :o, :host,    'specify the host to listen on', default: '127.0.0.1'
+required :p, :port,    'specify the port to listen on', transform: Nanoc::CLI::Transform::Port, default: 3000
 no_params
 
 runner Nanoc::Live::CommandRunners::Live
diff --git a/nanoc/NEWS.md b/nanoc/NEWS.md
index 77574cc..73c89a8 100644
--- a/nanoc/NEWS.md
+++ b/nanoc/NEWS.md
@@ -1,5 +1,11 @@
 # Nanoc news
 
+## 4.12.16 (2023-06-24)
+
+Fixes:
+
+- Made error handler print full error message if available (#1657)
+
 ## 4.12.15 (2023-02-06)
 
 Fixes:
diff --git a/nanoc/lib/nanoc/version.rb b/nanoc/lib/nanoc/version.rb
index 2712d76..73458ee 100644
--- a/nanoc/lib/nanoc/version.rb
+++ b/nanoc/lib/nanoc/version.rb
@@ -2,5 +2,5 @@
 
 module Nanoc
   # The current Nanoc version.
-  VERSION = '4.12.15'
+  VERSION = '4.12.16'
 end
diff --git a/nanoc/spec/nanoc/filters/colorize_syntax/rouge_spec.rb b/nanoc/spec/nanoc/filters/colorize_syntax/rouge_spec.rb
index 2b55fcf..e3b9c64 100644
--- a/nanoc/spec/nanoc/filters/colorize_syntax/rouge_spec.rb
+++ b/nanoc/spec/nanoc/filters/colorize_syntax/rouge_spec.rb
@@ -86,8 +86,8 @@ describe Nanoc::Filters::ColorizeSyntax, filter: true do
           let(:output) do
             <<~EOS
               before
-              <pre><code class="language-ruby">  <span style="color: #000000;font-weight: bold">def</span> <span style="color: #990000;font-weight: bold">foo</span>
-                <span style="color: #000000;font-weight: bold">end</span></code></pre>
+              <pre><code class="language-ruby">  <span style="color: #cf222e">def</span> <span style="color: #8250df">foo</span>
+                <span style="color: #cf222e">end</span></code></pre>
               after
             EOS
           end
diff --git a/nanoc/spec/nanoc/filters/less_spec.rb b/nanoc/spec/nanoc/filters/less_spec.rb
index b4c7f65..bf00a84 100644
--- a/nanoc/spec/nanoc/filters/less_spec.rb
+++ b/nanoc/spec/nanoc/filters/less_spec.rb
@@ -6,9 +6,6 @@ describe Nanoc::Filters::Less, site: true, stdio: true do
 
   before do
     skip_unless_gem_available('less')
-  end
-
-  before do
     File.open('Rules', 'w') do |io|
       io.write "compile '/**/*.less' do\n"
       io.write "  filter :less\n"
diff --git a/nanoc/spec/nanoc/helpers/blogging_spec.rb b/nanoc/spec/nanoc/helpers/blogging_spec.rb
index 67a4486..c588998 100644
--- a/nanoc/spec/nanoc/helpers/blogging_spec.rb
+++ b/nanoc/spec/nanoc/helpers/blogging_spec.rb
@@ -16,7 +16,7 @@ describe Nanoc::Helpers::Blogging, helper: true do
     end
 
     it 'returns the two articles' do
-      expect(subject.map(&:identifier)).to match_array(['/1', '/2'])
+      expect(subject.map(&:identifier)).to contain_exactly('/1', '/2')
     end
   end
 
diff --git a/nanoc/spec/nanoc/helpers/link_to_spec.rb b/nanoc/spec/nanoc/helpers/link_to_spec.rb
index c5736cc..93b3b2d 100644
--- a/nanoc/spec/nanoc/helpers/link_to_spec.rb
+++ b/nanoc/spec/nanoc/helpers/link_to_spec.rb
@@ -53,14 +53,11 @@ describe Nanoc::Helpers::LinkTo, helper: true do
     context 'with item' do
       before do
         ctx.create_item('content', {}, '/target')
+        ctx.create_rep(target, '/target.html')
       end
 
       let(:target) { ctx.items['/target'] }
 
-      before do
-        ctx.create_rep(target, '/target.html')
-      end
-
       it { is_expected.to eql('<a href="/target.html">Text</a>') }
     end
 
diff --git a/nanoc/spec/nanoc/integration/outdatedness_integration_spec.rb b/nanoc/spec/nanoc/integration/outdatedness_integration_spec.rb
index f1c66a4..5a8bc9f 100644
--- a/nanoc/spec/nanoc/integration/outdatedness_integration_spec.rb
+++ b/nanoc/spec/nanoc/integration/outdatedness_integration_spec.rb
@@ -21,9 +21,9 @@ describe 'Outdatedness integration', site: true, stdio: true do
           write '/bar.html'
         end
       EOS
-    end
 
-    before { Nanoc::CLI.run(%w[compile]) }
+      Nanoc::CLI.run(%w[compile])
+    end
 
     it 'shows default rep outdatedness' do
       expect { Nanoc::CLI.run(%w[show-data --no-color]) }.to(
@@ -93,9 +93,9 @@ describe 'Outdatedness integration', site: true, stdio: true do
           write '/bar.html'
         end
       EOS
-    end
 
-    before { Nanoc::CLI.run(%w[compile]) }
+      Nanoc::CLI.run(%w[compile])
+    end
 
     it 'shows default rep outdatedness' do
       expect { Nanoc::CLI.run(%w[show-data --no-color]) }.to(
@@ -137,9 +137,9 @@ describe 'Outdatedness integration', site: true, stdio: true do
           write '/bar.html'
         end
       EOS
-    end
 
-    before { Nanoc::CLI.run(%w[compile]) }
+      Nanoc::CLI.run(%w[compile])
+    end
 
     it 'shows default rep outdatedness' do
       expect { Nanoc::CLI.run(%w[show-data --no-color]) }.to(
@@ -199,9 +199,9 @@ describe 'Outdatedness integration', site: true, stdio: true do
           write '/bar.html'
         end
       EOS
-    end
 
-    before { Nanoc::CLI.run(%w[compile]) }
+      Nanoc::CLI.run(%w[compile])
+    end
 
     it 'shows default rep outdatedness' do
       expect { Nanoc::CLI.run(%w[show-data --no-color]) }.to(
diff --git a/nanoc/spec/nanoc/regressions/gh_1102_spec.rb b/nanoc/spec/nanoc/regressions/gh_1102_spec.rb
index fe55532..66cdeb6 100644
--- a/nanoc/spec/nanoc/regressions/gh_1102_spec.rb
+++ b/nanoc/spec/nanoc/regressions/gh_1102_spec.rb
@@ -4,15 +4,13 @@ describe 'GH-1102', site: true, stdio: true do
   before do
     File.write('content/index.html', '<%= "things" %>')
 
-    File.write('Rules', <<EOS)
-  compile '/**/*.html' do
-    filter :erb
-    write item.identifier.to_s
-  end
-EOS
-  end
+    File.write('Rules', <<~EOS)
+      compile '/**/*.html' do
+        filter :erb
+        write item.identifier.to_s
+      end
+    EOS
 
-  before do
     Nanoc::CLI.run(%w[compile])
   end
 
diff --git a/nanoc/spec/nanoc/regressions/gh_1216_spec.rb b/nanoc/spec/nanoc/regressions/gh_1216_spec.rb
index bd64169..91be851 100644
--- a/nanoc/spec/nanoc/regressions/gh_1216_spec.rb
+++ b/nanoc/spec/nanoc/regressions/gh_1216_spec.rb
@@ -13,9 +13,7 @@ describe 'GH-1216', site: true, stdio: true do
         write ext: 'html'
       end
     EOS
-  end
 
-  before do
     Nanoc::CLI.run(%w[compile])
   end
 
diff --git a/nanoc/spec/nanoc/regressions/gh_1248_spec.rb b/nanoc/spec/nanoc/regressions/gh_1248_spec.rb
index 585b6e8..4e3865f 100644
--- a/nanoc/spec/nanoc/regressions/gh_1248_spec.rb
+++ b/nanoc/spec/nanoc/regressions/gh_1248_spec.rb
@@ -11,9 +11,7 @@ describe 'GH-1248', site: true, stdio: true do
 
       passthrough '/**/*'
     EOS
-  end
 
-  before do
     Nanoc::CLI.run(%w[compile])
   end
 
diff --git a/nanoc/spec/nanoc/regressions/gh_1313_spec.rb b/nanoc/spec/nanoc/regressions/gh_1313_spec.rb
index 179b620..05735f8 100644
--- a/nanoc/spec/nanoc/regressions/gh_1313_spec.rb
+++ b/nanoc/spec/nanoc/regressions/gh_1313_spec.rb
@@ -9,9 +9,7 @@ describe 'GH-1313', site: true, stdio: true do
         exclude:
           - bin
     CONFIG
-  end
 
-  before do
     FileUtils.mkdir_p('build/bin/web/bin')
     File.write('build/bin/web/bin/should-be-pruned', 'asdf')
   end
diff --git a/nanoc/spec/nanoc/regressions/gh_1319_spec.rb b/nanoc/spec/nanoc/regressions/gh_1319_spec.rb
index e2188d6..4f477da 100644
--- a/nanoc/spec/nanoc/regressions/gh_1319_spec.rb
+++ b/nanoc/spec/nanoc/regressions/gh_1319_spec.rb
@@ -10,9 +10,7 @@ describe 'GH-1319', site: true, stdio: true do
         write ext: 'html'
       end
     EOS
-  end
 
-  before do
     Nanoc::CLI.run(%w[compile])
   end
 
diff --git a/nanoc/spec/nanoc/regressions/gh_1328_spec.rb b/nanoc/spec/nanoc/regressions/gh_1328_spec.rb
index e04f3f1..6b7be14 100644
--- a/nanoc/spec/nanoc/regressions/gh_1328_spec.rb
+++ b/nanoc/spec/nanoc/regressions/gh_1328_spec.rb
@@ -21,9 +21,7 @@ describe 'GH-1328', site: true, stdio: true do
         write ext: 'xhtml'
       end
     EOS
-  end
 
-  before do
     Nanoc::CLI.run([])
   end
 
diff --git a/nanoc/spec/nanoc/regressions/gh_776_spec.rb b/nanoc/spec/nanoc/regressions/gh_776_spec.rb
index 6bc02dc..fcd0317 100644
--- a/nanoc/spec/nanoc/regressions/gh_776_spec.rb
+++ b/nanoc/spec/nanoc/regressions/gh_776_spec.rb
@@ -4,23 +4,21 @@ describe 'GH-776', site: true do
   before do
     File.write('content/donkey.md', 'Donkey!')
 
-    File.write('Rules', <<EOS)
-  compile '/donkey.*' do
-    filter :erb
-    snapshot :secret, path: '/donkey-secret.html'
-    write '/donkey.html'
-  end
-
-  layout '/foo.*', :erb
-EOS
-  end
+    File.write('Rules', <<~EOS)
+      compile '/donkey.*' do
+        filter :erb
+        snapshot :secret, path: '/donkey-secret.html'
+        write '/donkey.html'
+      end
 
-  let(:site) { Nanoc::Core::SiteLoader.new.new_from_cwd }
+      layout '/foo.*', :erb
+    EOS
 
-  before do
     Nanoc::Core::Compiler.compile(site)
   end
 
+  let(:site) { Nanoc::Core::SiteLoader.new.new_from_cwd }
+
   context 'without pruning' do
     it 'writes two files' do
       expect(File.read('output/donkey.html')).to eql('Donkey!')
diff --git a/nanoc/spec/nanoc/regressions/gh_924_spec.rb b/nanoc/spec/nanoc/regressions/gh_924_spec.rb
index eebf539..a9e704a 100644
--- a/nanoc/spec/nanoc/regressions/gh_924_spec.rb
+++ b/nanoc/spec/nanoc/regressions/gh_924_spec.rb
@@ -55,9 +55,7 @@ describe 'GH-924', site: true, stdio: true do
 
       layout '/**/*.xsl', :xsl
     EOS
-  end
 
-  before do
     Nanoc::CLI.run(%w[compile])
   end
 
diff --git a/nanoc/spec/nanoc/regressions/gh_970b_spec.rb b/nanoc/spec/nanoc/regressions/gh_970b_spec.rb
index 0f15b77..5c7edb1 100644
--- a/nanoc/spec/nanoc/regressions/gh_970b_spec.rb
+++ b/nanoc/spec/nanoc/regressions/gh_970b_spec.rb
@@ -15,9 +15,9 @@ describe 'GH-970 (show-data)', site: true, stdio: true do
         write '/bar.html'
       end
     EOS
-  end
 
-  before { Nanoc::CLI.run(%w[compile]) }
+    Nanoc::CLI.run(%w[compile])
+  end
 
   it 'shows default rep outdatedness' do
     expect { Nanoc::CLI.run(%w[show-data --no-color]) }.to(
diff --git a/nanoc/spec/nanoc/rule_dsl/rule_context_spec.rb b/nanoc/spec/nanoc/rule_dsl/rule_context_spec.rb
index 86d6a38..ddbfb19 100644
--- a/nanoc/spec/nanoc/rule_dsl/rule_context_spec.rb
+++ b/nanoc/spec/nanoc/rule_dsl/rule_context_spec.rb
@@ -115,7 +115,7 @@ shared_examples 'a rule context' do
     end
 
     it 'contains all items' do
-      expect(subject._unwrap).to match_array([item, parent, child])
+      expect(subject._unwrap).to contain_exactly(item, parent, child)
     end
 
     it 'provides no rep access' do
diff --git a/nanoc/test/base/test_site.rb b/nanoc/test/base/test_site.rb
index 8909f53..33dc716 100644
--- a/nanoc/test/base/test_site.rb
+++ b/nanoc/test/base/test_site.rb
@@ -99,11 +99,11 @@ class Nanoc::Core::SiteTest < Nanoc::TestCase
       site = Nanoc::Core::SiteLoader.new.new_from_cwd
 
       site.items.each do |item|
-        assert_equal Nanoc::Core::Identifier, item.identifier.class
+        assert_instance_of Nanoc::Core::Identifier, item.identifier
       end
 
       site.layouts.each do |layout|
-        assert_equal Nanoc::Core::Identifier, layout.identifier.class
+        assert_instance_of Nanoc::Core::Identifier, layout.identifier
       end
     end
   end
diff --git a/nanoc/test/data_sources/test_filesystem.rb b/nanoc/test/data_sources/test_filesystem.rb
index 04ae0e1..61c5811 100644
--- a/nanoc/test/data_sources/test_filesystem.rb
+++ b/nanoc/test/data_sources/test_filesystem.rb
@@ -110,7 +110,7 @@ class Nanoc::DataSources::FilesystemTest < Nanoc::TestCase
     assert_equal 1, items.size
     assert_predicate items[0].content, :binary?
     assert_equal "#{Dir.getwd}/foo/stuff.dat", items[0].content.filename
-    assert_equal Nanoc::Core::BinaryContent, items[0].content.class
+    assert_instance_of Nanoc::Core::BinaryContent, items[0].content
   end
 
   def test_load_layouts_with_nil_dir_name
@@ -631,7 +631,7 @@ class Nanoc::DataSources::FilesystemTest < Nanoc::TestCase
     assert_equal ['./stuff/foo'], res.keys
     assert_equal 2, res.values[0].size
     assert_equal 'yaml', res.values[0][0]
-    assert_equal Array, res.values[0][1].class
+    assert_instance_of Array, res.values[0][1]
     assert_equal %w[html md], res.values[0][1].sort
   end
 
diff --git a/nanoc/test/rule_dsl/test_compiler_dsl.rb b/nanoc/test/rule_dsl/test_compiler_dsl.rb
index 23d3bc0..2c6c9ba 100644
--- a/nanoc/test/rule_dsl/test_compiler_dsl.rb
+++ b/nanoc/test/rule_dsl/test_compiler_dsl.rb
@@ -277,9 +277,13 @@ class Nanoc::RuleDSL::CompilerDSLTest < Nanoc::TestCase
 
     pattern = compiler_dsl.create_pattern('/foo/*')
 
+    # rubocop:disable Minitest/AssertMatch
+    # rubocop:disable Minitest/RefuteMatch
     assert pattern.match?('/foo/aaaa')
     refute pattern.match?('/foo/aaaa/')
     refute pattern.match?('/foo/a/a/a/a')
+    # rubocop:enable Minitest/RefuteMatch
+    # rubocop:enable Minitest/AssertMatch Minitest/RefuteMatch
   end
 
   def test_create_pattern_with_regex
@@ -287,7 +291,9 @@ class Nanoc::RuleDSL::CompilerDSLTest < Nanoc::TestCase
 
     pattern = compiler_dsl.create_pattern(%r{\A/foo/a*/})
 
+    # rubocop:disable Minitest/AssertMatch
     assert pattern.match?('/foo/aaaa/')
+    # rubocop:enable Minitest/AssertMatch
   end
 
   def test_create_pattern_with_string_with_unknown_string_pattern_type
@@ -389,8 +395,13 @@ class Nanoc::RuleDSL::CompilerDSLTest < Nanoc::TestCase
     assert_equal(expected.source,    actual.source)
     assert_equal(expected.casefold?, actual.casefold?)
     assert_equal(expected.options,   actual.options)
+
+    # rubocop:disable Minitest/AssertMatch
+    # rubocop:disable Minitest/RefuteMatch
     assert('/foo/bar/' =~ actual)
     refute('/foo/' =~ actual)
+    # rubocop:enable Minitest/RefuteMatch
+    # rubocop:enable Minitest/AssertMatch
   end
 
   def test_identifier_to_regex_with_full_identifier
@@ -402,9 +413,13 @@ class Nanoc::RuleDSL::CompilerDSLTest < Nanoc::TestCase
 
     assert_equal(expected.to_s, actual.to_s)
 
+    # rubocop:disable Minitest/AssertMatch
+    # rubocop:disable Minitest/RefuteMatch
     assert('/favicon.ico' =~ actual)
     assert('/favicon.ico/' =~ actual)
     refute('/faviconxico' =~ actual)
+    # rubocop:enable Minitest/RefuteMatch
+    # rubocop:enable Minitest/AssertMatch
   end
 
   def test_dsl_has_no_access_to_compiler

More details

Full run details

Historical runs