Skip to content

Treat empty unit string as dimensionless in conversions#34

Merged
nertzy merged 1 commit into
minad:masterfrom
nertzy:empty-unit-string-conversion
Jun 23, 2026
Merged

Treat empty unit string as dimensionless in conversions#34
nertzy merged 1 commit into
minad:masterfrom
nertzy:empty-unit-string-conversion

Conversation

@nertzy

@nertzy nertzy commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Unit#in!("") (and in!(" "), and the same via #in) crashed with
NoMethodError: undefined method 'each' for nil instead of behaving like the
dimensionless unit. Construction already treats an empty unit string as
dimensionless (Unit(1, "") # => Unit("1")), so conversion was inconsistent
with construction
for the same input.

Unit(1, "")          # => Unit("1")    construction: "" is dimensionless, fine
Unit(1, "").in!(nil) # => TypeError    clean (coercion guard)
Unit(1, "").in!("")  # => NoMethodError: undefined method 'each' for nil   BUG
Unit(1, "").in!(" ") # => NoMethodError: undefined method 'each' for nil   BUG

A rescue TypeError around a conversion used as a dimensional guard therefore
did not catch the empty-string case — it slipped past the coercion guard and
crashed deeper.

Root cause

Unit::System#parse_unit returned result.last. For an empty or
whitespace-only expression the tokenizer yields no tokens, so result is []
and result.last is nil. validate_unit then called nil.each.

Fix

Make parse_unit total: an empty / whitespace-only expression yields the
dimensionless unit definition [] (the shape every consumer — to_unit, the
constructor, validate_unit — already accepts) rather than nil. So:

Unit(1, "").in("")   # => Unit("1")     now matches construction
Unit(1, "").in!("")  # => Unit("1")
Unit(1, "").in!(" ") # => Unit("1")
Unit(5, "m/s").in!("") # => TypeError   clean, not NoMethodError

Test plan

  • bundle exec rake — both phases green (160 no-DSL + 162 DSL examples, 0 failures)
  • New specs cover #in("") / #in(" ") and #in!("") / #in!(" ") returning
    the dimensionless unit, a clean TypeError from #in! for an incompatible unit,
    and parse_unit("") / parse_unit(" ") returning []

Unit#in!("") (and in!(" "), and the same via #in) crashed with
NoMethodError: undefined method 'each' for nil instead of behaving like
the dimensionless unit. Construction already treats an empty unit string
as dimensionless (Unit(1, "") => Unit("1")), so conversion was
inconsistent with construction for the same input.

System#parse_unit returned result.last, which is nil for an empty or
whitespace-only expression (the tokenizer yields no tokens). validate_unit
then called nil.each. Make parse_unit total by returning [] -- the
dimensionless unit shape every consumer (to_unit, the constructor,
validate_unit) already accepts -- instead of nil.
@nertzy nertzy force-pushed the empty-unit-string-conversion branch from 4692ae7 to 6b12583 Compare June 23, 2026 21:49
@nertzy nertzy merged commit d7e4dc3 into minad:master Jun 23, 2026
4 checks passed
@nertzy nertzy deleted the empty-unit-string-conversion branch June 23, 2026 21:50
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.

1 participant