Skip to content

Comments

LeakyConstantDeclaration - allow definitions on example group#1798

Merged
pirj merged 1 commit intorubocop:masterfrom
naveg:define-on-example-group
Jun 20, 2025
Merged

LeakyConstantDeclaration - allow definitions on example group#1798
pirj merged 1 commit intorubocop:masterfrom
naveg:define-on-example-group

Conversation

@naveg
Copy link
Contributor

@naveg naveg commented Feb 5, 2024

Constant, classes, and modules that are defined on the example group do
not leak into the global namespace. While doing this may not be
a particularly good practice, it should ideally not result in
an offense.

The practice is described here: https://makandracards.com/makandra/47189-rspec-how-to-define-classes-for-specs#section-1-defining-the-constant-on-the-example-class


Before submitting the PR make sure the following are checked:

  • Feature branch is up-to-date with master (if not - rebase it).
  • Squashed related commits together.
  • Added tests.
  • Updated documentation.
  • Added an entry to the CHANGELOG.md if the new code introduces user-observable changes.
  • The build (bundle exec rake) passes (be sure to run this locally, since it may produce updated documentation that you will need to commit).

If you have created a new cop:

  • Added the new cop to config/default.yml.
  • The cop is configured as Enabled: pending in config/default.yml.
  • The cop is configured as Enabled: true in .rubocop.yml.
  • The cop documents examples of good and bad code.
  • The tests assert both that bad code is reported and that good code is not reported.
  • Set VersionAdded: "<<next>>" in default/config.yml.

If you have modified an existing cop's configuration options:

  • Set VersionChanged: "<<next>>" in config/default.yml.

@naveg naveg force-pushed the define-on-example-group branch 2 times, most recently from 51f9e38 to b11854b Compare February 5, 2024 23:19
@naveg naveg marked this pull request as ready for review February 5, 2024 23:21
@naveg naveg requested a review from a team as a code owner February 5, 2024 23:21
@pirj
Copy link
Member

pirj commented Feb 6, 2024

Sorry, I didn’t read the article, but the description of this is misleading.
With self:: (or anything:: really), you explicitly specify the owner of the constant.
The problem that this cop solves is auto-detection of the owner that defaults to the top-level namespace, which is unintuitive and confusing.

I’m fine to accept this if we change the scope to ignore all const definitions with explicitly specified owner.
Or is it problematic for examples?

@pirj
Copy link
Member

pirj commented Feb 6, 2024

The problem with the approach described in the article is that examples may litter class, and those changes would persist between examples.

The cop was initially introduced to avoid side effects and surprises. I’d love it to keep what it’s doing. https://fili.pp.ru/leaky-constants.html

I’m more inclined to close the PR. WDYT @rubocop/rubocop-rspec?

@naveg
Copy link
Contributor Author

naveg commented Feb 6, 2024

The problem with the approach described in the article is that examples may litter class, and those changes would persist between examples.

This is true of any class defined in any location. The issue flagged by the cop is that it's quite easy to define classes in the global namespace without realizing that is happening. Using self:: (or any other namespace, as you point out) resolves that.

Allowing any explicitly specified namespace would make sense to me - even ::. If you do that intentionally, the cop should respect that.

@pirj
Copy link
Member

pirj commented Feb 6, 2024

This is true of any class defined in any location.

let(:klass) { Class.new { } } won’t suffer from this.
But in general - yes, it’s possible to use regular variables in example groups like klass = “hello”, and it would be possible to alter it, too. This is why we have lets.

Allowing any explicitly specified namespace would make sense to me - even ::. If you do that intentionally, the cop should respect that.

Agree.

@naveg naveg force-pushed the define-on-example-group branch 2 times, most recently from 9b897fb to cdbfcce Compare June 3, 2025 20:33
@naveg
Copy link
Contributor Author

naveg commented Jun 3, 2025

@pirj I have finally returned to this one and updated it to allow all constants defined with explicit namespaces. Do you think this is a reasonable balance between avoiding the global namespace footgun, and providing an escape hatch for code authors?

I'm not sure how to resolve the code coverage failure - I think that I have tests for everything

it 'ignores classes defined explicitly in the global namespace' do
expect_no_offenses(<<~RUBY)
describe SomeClass do
::CONSTANT = "Accessible as ::CONSTANT".freeze
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also accessible as CONSTANT aka “I know what I am doing”.

Copy link
Member

@pirj pirj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks reasonable, thanks for bringing this up.

@pirj pirj requested review from Darhazer, bquorning, koic and ydah June 7, 2025 16:36
@pirj
Copy link
Member

pirj commented Jun 7, 2025

resolve the code coverage failure

What’s in coverage/index.html if you run specs locally?

@bquorning
Copy link
Collaborator

What’s in coverage/index.html if you run specs locally?

It’s the subtle “not covering the implicit else” that we’ve seen a few times before. This time in

elsif node.is_a?(RuboCop::AST::CasgnNode)

Since the conditional covers the only 3 types of nodes that can reach this method, I think you should add the following lines to the method:

else
  # :nocov:
  :noop
  # :nocov:

@naveg naveg force-pushed the define-on-example-group branch 3 times, most recently from 6def7ff to ce46ba9 Compare June 9, 2025 17:19
Copy link
Member

@pirj pirj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@naveg naveg requested a review from bquorning June 13, 2025 16:29
@naveg naveg requested a review from pirj June 13, 2025 16:29
@naveg
Copy link
Contributor Author

naveg commented Jun 19, 2025

@pirj is there anything else I can do to help this r get merged and released?

@bquorning
Copy link
Collaborator

Could you please squash the commits into one. Otherwise, I think it’s ready to merge.

Constant, classes, and modules that are defined on explicit namespaces
do not "leak" into the global namespace, because the author is making
their intention clear. This is even true when when explicitly using
`::`. While doing this may not be a particularly good practice, it
should ideally not result in an offense.

The practice is described here: https://makandracards.com/makandra/47189-rspec-how-to-define-classes-for-specs#section-1-defining-the-constant-on-the-example-class
@naveg naveg force-pushed the define-on-example-group branch from cb8838e to 568cd20 Compare June 20, 2025 19:07
@naveg
Copy link
Contributor Author

naveg commented Jun 20, 2025

@bquorning done, thanks!

@pirj pirj merged commit bb168ba into rubocop:master Jun 20, 2025
27 checks passed
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.

3 participants