Skip to content

#481: Handle both string and boolean forms in Fbe.pmp bool coercion#498

Open
VasilevNStas wants to merge 4 commits into
zerocracy:masterfrom
VasilevNStas:fix/481-pmp-bool-coercion
Open

#481: Handle both string and boolean forms in Fbe.pmp bool coercion#498
VasilevNStas wants to merge 4 commits into
zerocracy:masterfrom
VasilevNStas:fix/481-pmp-bool-coercion

Conversation

@VasilevNStas

@VasilevNStas VasilevNStas commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Description

In lib/fbe/pmp.rb, bool parameter values are coerced twice:

  1. Line 86: XML <default> string is converted to Ruby boolean via default == 'true'
  2. Line 90: result ||= default — when no factbase override exists, result becomes the already-coerced Ruby boolean
  3. Line 95: Second coercion: result == 'true' — compares the value to string 'true'

The problem: when result is a Ruby true (from the default), true == 'true' is false in Ruby. So any bool parameter whose XML default is true silently becomes false when no factbase override exists.

The bug is latent because assets/pmp.xml currently has only one bool parameter: communications.stealth with default false — which happens to produce the correct result by accident (false == 'true' is false).

Fix

Changed line 95 from:

when 'bool' then result == 'true'

to:

when 'bool' then result.to_s == 'true'

result.to_s == 'true' correctly handles:

  • Ruby booleans: true.to_s == 'true'true
  • Strings from factbase: 'true'.to_s == 'true'true
  • nil: nil.to_s == 'true'false (correct)

Verification

  • bundle exec rubocop — 0 offenses
  • bundle exec rake test — 283 tests, 0 failures

Closes #481

@edmoffo edmoffo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fix on lib/fbe/pmp.rb:95 matches one of the candidates suggested in #481 and the conversion is sound (true.to_s == 'true' is true, 'true'.to_s == 'true' is true, false.to_s == 'true' is false). The defect, however, was latent specifically because no test exercises a bool parameter whose XML default is true. Add a regression test that loads a PMP definition with a true bool default and asserts Fbe.pmp(...).<area>.<param> returns true when the factbase has no override — without it, the next refactor of the same code path will not catch this again.

The branch also downgrades crate-ci/typos from v1.47.1 to v1.47.0 in .github/workflows/typos.yml. Drop that hunk; it is unrelated to #481.

@kreinba kreinba left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The .to_s coercion makes the line idempotent for both the Ruby boolean coming from line 86 and the string coming from the factbase, which is the right shape of the fix. One inline note about adding a regression test that locks in the true-default behavior.

Comment thread lib/fbe/pmp.rb Outdated
when 'int' then Integer(Float(result).truncate)
when 'float' then Float(result)
when 'bool' then result == 'true'
when 'bool' then result.to_s == 'true'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The fix is correct but the existing test suite cannot exercise it: assets/pmp.xml only declares one bool param (communications.stealth) with default false, and test_reads_false_boolean / test_reads_true_boolean both write a factbase override so they never traverse the result ||= default path with a true XML default. The original bug would reappear unnoticed the next time someone adds a bool with default true. Either temporarily inject a stub pmp.xml in a new test, or stub the Nokogiri tree so a bool param with default true is read with an empty factbase and asserted true.

@VasilevNStas

Copy link
Copy Markdown
Contributor Author

All reviewer comments have been addressed. Please take another look when you have a moment. @edmoffo @kreinba

@VasilevNStas VasilevNStas force-pushed the fix/481-pmp-bool-coercion branch from ae0621d to de94b43 Compare June 3, 2026 19:26
@VasilevNStas VasilevNStas changed the title fix: handle both string and boolean forms in Fbe.pmp bool coercion #481 fix: handle both string and boolean forms in Fbe.pmp bool coercion Jun 8, 2026
@VasilevNStas VasilevNStas changed the title #481 fix: handle both string and boolean forms in Fbe.pmp bool coercion #481: Handle both string and boolean forms in Fbe.pmp bool coercion Jun 8, 2026
@yegor256

Copy link
Copy Markdown
Member

@VasilevNStas merge conflicts here

@yegor256

Copy link
Copy Markdown
Member

Please fix the merge conflicts so this pull request can be merged.

The second bool coercion in Fbe.pmp (line 95) compared the value
to string 'true' using result == 'true'. When a bool parameter's
XML default is 'true', it gets coerced to a Ruby boolean on first
pass (line 86: default == 'true'), then result ||= default sets
result to true on line 90. The second pass then compares true == 'true',
which is always false in Ruby, silently flipping true to false.

Changed to result.to_s == 'true', which correctly handles both:
- Ruby booleans from the already-coerced XML default
- Strings coming from factbase overrides (f[param]&.first)

The bug is latent because assets/pmp.xml only has one bool parameter,
stealth, with default false.
@VasilevNStas VasilevNStas force-pushed the fix/481-pmp-bool-coercion branch from de94b43 to ea2d6d6 Compare June 13, 2026 15:22
@VasilevNStas

Copy link
Copy Markdown
Contributor Author

@yegor256 plz review

@VasilevNStas VasilevNStas requested a review from edmoffo June 13, 2026 15:33
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.

Fbe.pmp coerces bool params twice; true XML defaults silently become false when the factbase has no entry

4 participants