rubocop-hash_inspect

A RuboCop extension gem that flags Ruby code relying on the legacy (Ruby <= 3.3) Hash#inspect output format. Ruby 3.4 changed Hash#inspect from {:x=>1} to {x: 1}, breaking tests that hardcode the old rocket-syntax form. This cop catches those literals statically during pdk validate so Puppet module authors can fix them before upgrading to Puppet 9 (Ruby 4).

Why This Matters — Ruby 3.4 Hash#inspect Change

Ruby 3.4 (December 2024) changed the output of Hash#inspect:

  • Ruby <= 3.3 (Puppet 8): {:x=>1, "baz"=>3}
  • Ruby >= 3.4 (Puppet 9 / Ruby 4): {x: 1, "baz" => 3}

Code that hardcodes the old format in test assertions silently breaks on Puppet 9. This cop detects the legacy rocket-syntax format inside string and regexp literals.

Installation

Add to your Gemfile (development group):

gem 'rubocop-hash_inspect', '~> 0.2', require: false

Then add to your .rubocop.yml (preferred — requires RuboCop >= 1.72):

plugins:
  - rubocop-hash_inspect

Or using the legacy require: directive (compatible with all RuboCop versions):

require:
  - rubocop-hash_inspect

Usage

Once loaded, the cop HashInspect/LegacyHashInspectFormat runs automatically as part of rubocop (or pdk validate when wired via pdk-templates).

Cop: HashInspect/LegacyHashInspectFormat

Flags string, regexp, and interpolated-string literals that contain the legacy Hash#inspect rocket-syntax format ({:sym=>val}), which breaks on Ruby 3.4 / Puppet 9.

Example — flagged (legacy format)

# Legacy format — breaks on Ruby 3.4 / Puppet 9
expect(result.inspect).to eq("{:a=>1}")
expect(msg).to match(/\{:name=>"foo"\}/)

Example — correct

# New format — correct on Ruby >= 3.4
expect(result.inspect).to eq("{a: 1}")
expect(msg).to match(/\{name: "foo"\}/)
# Or better: use a matcher that doesn't depend on inspect output:
expect(result).to eq({ a: 1 })

Offense message

Legacy \Hash#inspect` format (`:sym=>...`). Ruby 3.4+ renders hashes as `...`, so this hardcoded value breaks on Ruby 3.4 / Puppet 9. Update it to the new format.`

Disable or configure

# Disable for a specific file (inline):
# rubocop:disable HashInspect/LegacyHashInspectFormat

# Disable globally in .rubocop.yml:
HashInspect/LegacyHashInspectFormat:
  Enabled: false

Development

After checking out the repo, run bin/setup to install dependencies. Then run bundle exec rake to run the spec suite and self-lint together.

bin/setup
bundle exec rake          # spec + self-lint (default task)
bundle exec rake spec     # specs only
bundle exec rake rubocop  # self-lint only

To generate a new cop skeleton:

bundle exec rake 'new_cop[HashInspect/CopName]'

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/puppetlabs/rubocop-hash_inspect.

License

This gem is available as open source under the terms of the Apache License 2.0.