Module: LcpRuby::Dsl::SourceLocationCapture

Overview

Mixin used by DSL builders to capture the source location (file + line) of the caller when a labeled DSL setter executes.

The captured info travels with the parsed Definition into ‘LcpRuby.loader.*_definitions` so the `i18n_check` walker can point at the exact DSL line that introduced a literal label. YAML-loaded definitions never enter a builder, so they carry `source: nil`; the walker falls back to `definition.source_path` (file context, line nil) for those.

Why ‘caller_locations(2, 1)`: builders are usually called from a `define_*` block, where the call stack at the setter site is `[ setter_method, dsl_block, define_*_loader ]`. We want the DSL block’s frame — the file/line in the host’s ‘config/lcp_ruby/*.rb` source — which sits one frame above the immediate caller.

Class Method Summary collapse

Class Method Details

.capture_source_loc(skip: 1) ⇒ Object

Returns ‘{ “file” => <abs path>, “line” => <1-based int> }` for the nearest user-DSL frame on the stack, or `nil` when called outside a DSL context.

**String keys are deliberate** — captured source locations travel with the rest of the builder hash through ‘to_hash` →`HashUtils.stringify_deep` → `from_hash`, where Symbol keys get converted to strings anyway. Using strings from the start keeps the iterator-yield shape stable across DSL-loaded vs YAML-loaded definitions.

‘skip` is the number of internal frames between this helper and the DSL block — most builder setters call `capture_source_loc` directly, so the default `skip: 1` puts us one frame above the setter and into the DSL block. Builders calling through a private helper should bump this.



39
40
41
42
43
44
45
46
47
48
49
# File 'lib/lcp_ruby/dsl/source_location_capture.rb', line 39

def capture_source_loc(skip: 1)
  # `caller_locations(skip + 1, 1)` — the +1 is for the call frame
  # of `capture_source_loc` itself.
  location = caller_locations(skip + 1, 1)&.first
  return nil unless location

  {
    "file" => location.absolute_path || location.path,
    "line" => location.lineno
  }
end