Skip to content

Add RuboCop cop enforcing Data.define over Struct.new - #2879

Merged
mroderick merged 4 commits into
masterfrom
feat/prefer-data-define-cop
Sep 15, 2026
Merged

mroderick merged 4 commits into
masterfrom
feat/prefer-data-define-cop

Conversation

@mroderick

@mroderick mroderick commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

Closes #2878.

Adds a custom RuboCop cop, Style/PreferDataDefine, that flags Struct.new sends with no autocorrection. The seven Struct.new test doubles in spec/helpers/email_header_helper_spec.rb are now RSpec verifying doubles (instance_double(Member, ...)), per review feedback — the repo's only Struct.new usages are gone. Follows Olle's review request on #2855.

The cop is required from .rubocop.yml, so bundle exec rubocop and CI enforce the preference from now on. A review round also fixed Zeitwerk eager-loading of lib/rubocop under CI. No autocorrection on purpose: the Struct.new -> Data.define rewrite isn't safely mechanical (mutability and keyword semantics differ), so each future hit gets a human decision.

Review notes

  • One deviation from the issue's snippet: ConstNode#value no longer exists in rubocop-ast 1.50, the cop uses const_name == 'Struct' instead (verified for both Struct and ::Struct; namespaced receivers like Other::Struct.new are correctly ignored).
  • Known blind spots, accepted per the issue's intent: AllCops.Exclude (db/config/bin) is not covered; a future --regenerate-todo while offenses exist would add a todo entry and suppress the cop; name-based indirection (s = Struct; s.new) bypasses it.

Requested in review on PR #2855: prefer Data.define over Struct.new
for immutable value objects. The custom cop flags Struct.new sends
without autocorrection (mutability and keyword semantics differ, so
each hit needs a human decision) and is required from .rubocop.yml.

Converts the seven Struct.new test doubles in
spec/helpers/email_header_helper_spec.rb to Data.define — the only
usages in the repo. Data.define accepts the same positional
arguments here.

Refs #2878
CI eager-loads the app (config.eager_load = ENV['CI'].present? in
test.rb), and autoload_lib walked lib/rubocop, requiring the cop
before rubocop was loaded: 'uninitialized constant
RuboCop::Cop::Style::Base'. RuboCop cops are loaded by RuboCop itself
via the require in .rubocop.yml, not by Rails; exclude the directory
from autoloading, as the autoload_lib comment invites.
@mroderick
mroderick marked this pull request as ready for review September 15, 2026 10:46
Comment thread spec/helpers/email_header_helper_spec.rb Outdated
Review feedback on #2879: the spec's member stand-ins should be RSpec
doubles rather than Data.define instances. instance_double(Member)
verifies against the real class and satisfies RSpec/VerifiedDoubles.
The helper only reads member.id and member.email.
@mroderick
mroderick enabled auto-merge September 15, 2026 15:17
@mroderick
mroderick merged commit aadb505 into master Sep 15, 2026
9 checks passed
@mroderick
mroderick deleted the feat/prefer-data-define-cop branch September 15, 2026 15:21
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.

Add RuboCop cop enforcing Data.define over Struct.new and convert existing usages

2 participants