Skip to content

Conversation

@huwd
Copy link
Contributor

@huwd huwd commented Aug 1, 2025

Fixes: #93

The ruby Hash#delete function will remove a value by key from the hash and return the associated value:
https://ruby-doc.org/3.4.1/Hash.html#method-i-delete

Where there is no value it'll return nil.

It seems that normally this is not an issue because Middelman::Syntax::Highlighter sets :lexer_options with a default {} in line /lib/middleman-syntax/extension.rb:16

However some folks might be calling super, might have changed Highlighter.options or otherwise mucked around with opts or options in a way that makes the assumption of presence here unreliable under some circumstances.

There are likely a few ways to deal with this, whilst highlighter_options gets passed further down, I can't find any evidence that the key :lexer_options gets called with any assumptions beyond that point. So it might just be safe to replace .delete with .dig here.

However as I'm not a maintainer here, I thought I'd suggest the minimum viable change which is to add a guard clauses that overrides the line if nil is returned (suggesting the :lexer_options key wasn't present) and passes an empty hash instead.

This should prevent Lexer throwing an error on initialisation, and allow other functionality to continue as normal.

I tried to figure out a test that proves this case, however all attempts seemed to really go against the testing flow of the repo (for example by adding unit tests and not just relying on feature tests), or found a struggle with overriding the default {} set for lexer_options set in line lib/middleman-syntax/extension.rb:16.

Which leads to the interesting question of, how have a trigged this bug in the first place? As far as I can tell it's because we call super on the class and muck around a bit from within our own repo.

So the slightly awkward test here is to use Gemfile :path to test this against our repo. Which works. Sorry to not have added a better regression, but perhaps would need a bit more of a talk about approach to best surface an issue like this.

Will leave it to maintainers to decide if that's good enough or if we should figure that out before merging this.

The ruby Hash#delete function will remove a value by key from the
hash and return the associated value:
https://ruby-doc.org/3.4.1/Hash.html#method-i-delete

Where there is no value it'll return nil.

It seems that normally this is not an issue because
Middelman::Syntax::Highlighter sets :lexer_options with a default {}
in line /lib/middleman-syntax/extension.rb:16

However some folks might be calling super, might have changed
Highlighter.options or otherwise mucked around with opts or options
in a way that makes the assumption of presence here unreliable under
some circumstances.

There are likely a few ways to deal with this, whilst highlighter_options
gets passed further down, I can't find any evidence that the key :lexer_options
gets called with any assumptions beyond that point. So it might just be safe
to replace .delete with .dig here.

However as I'm not a maintainer here, I thought I'd suggest the minimum viable
change which is to add a guard clasuse that overrides the line if `nil` is returned
(suggesting the :lexer_options key wasn't present) and passes an empty hash instead.

This should prevent Lexer throwing an error on initalisation, and allow other
functionality to continue as normal.

I tried to figure out a test that proves this case, however all attempts seemed to
really go against the testing flow of the repo (for example by adding unit tests
and not just relying on feature tests), or found a struggle with overriding the default
{} set for lexer_options set in line lib/middleman-syntax/extension.rb:16.

Which leads to the interesting question of, how have a triggerd this bug in the first
place? As far as I can tell it's because we call `super` on the class and muck around
a bit from within our own repo.

So the slightly awkward test here is to use Gemfile :path to test this against our
repo. Which works. Sorry to not have added a better regression, but perhaps would
need a bit more of a talk about approach to best surface an issue like this.

Will leave it to maintainers to decide if that's good enough or if we should figure that
out before merging this.
@huwd
Copy link
Contributor Author

huwd commented Aug 1, 2025

Had a crack, ran into some trouble with the tests.
I suspect because I've only triggered the bug because we're calling super in our repo.

Rather than introduce new unit tests, do some grim monkey patching in cucumber or shift too far off the existing test strategy I've just decided to put the guard clause in as a bit of a defensive programming fix.

Totally happy to discuss if that doesn't wash with you folks as maintainers.

@markets markets merged commit 7d88b14 into middleman:master Aug 2, 2025
6 checks passed
@markets
Copy link
Member

markets commented Aug 4, 2025

@huwd Just released a new version with this fix:

Thanks! 🙌🏼

huwd added a commit to alphagov/tech-docs-gem that referenced this pull request Aug 4, 2025
v3.6 actually had an issue that has been solved by the release of
v3.6.1 it was causing our tests to fail here, but has been fixed
upstream:

https://github.com/middleman/middleman-syntax/releases/tag/v3.6.1
middleman/middleman-syntax#94

Theoretically doing nothing is fine, but thought, why not push this up
to 3.6+ whilst we're at it. Especially if thats what we're testing
against now with new Lexer options
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

If :lexer_options is not present in Highlighter.highlight opts then Lexer throws an error

2 participants