Skip to content

Per-call override for value_humanizer #167

Description

@eitoball

Summary

Allow per-call override of header humanization instead of relying solely on the mutable class attribute HeaderExtractor.value_humanizer.

Problem

Header labels are derived via a global, mutable lambda:

# lib/comma/header_extractor.rb
class_attribute :value_humanizer
self.value_humanizer = DEFAULT_VALUE_HUMANIZER

Specs and apps that customize it must remember to reset global state afterward, or they leak behavior into other examples/processes.

This is fragile in:

  • Parallel test runners
  • Applications that need different humanizers per export
  • Library code that temporarily patches the humanizer

Proposed approach

Keep value_humanizer as the default, but allow an optional override at call time:

# Option A: keyword on existing methods
object.to_comma_headers(:default, humanizer: ->(value, model) { ... })

# Option B: thread-local fallback (less explicit, harder to test)

Thread the override through:

  • Object#to_comma_headers / extract_with
  • Comma::HeaderExtractor#initialize
  • Optionally Comma::Generator when writing header row (if collection-level override is desired)

Do not remove the class attribute — that would break existing global customization.

Files likely involved

  • lib/comma/header_extractor.rb
  • lib/comma/object.rb
  • lib/comma/generator.rb (if collection-level override)
  • spec/comma/header_extractor_spec.rb
  • New examples for override + reset-without-global-mutation

Acceptance criteria

  • Default humanization unchanged when no override passed
  • Per-call humanizer: override affects only that extraction
  • Global HeaderExtractor.value_humanizer = ... still works as today
  • Spec demonstrates override without mutating global state
  • Document new keyword in README (optional small doc PR)

Labels (suggested)

enhancement, refactor

Depends on

None.

Out of scope

  • Removing the class-level value_humanizer entirely (breaking change)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions