Class: TreeHaver::Backends::Psych::Node

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/tree_haver/backends/psych.rb

Overview

Psych node wrapper

Wraps Psych::Nodes::* classes to provide TreeHaver::Node-compatible interface.

Psych node types:

  • Stream: Root container

  • Document: YAML document (multiple per stream possible)

  • Mapping: Hash/object

  • Sequence: Array/list

  • Scalar: Primitive value (string, number, boolean, null)

  • Alias: YAML anchor reference

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(node, source, lines = nil) ⇒ Node

Create a new node wrapper

Parameters:

  • node (::Psych::Nodes::Node)

    Psych node

  • source (String)

    Original source

  • lines (Array<String>) (defaults to: nil)

    Source lines for text extraction



246
247
248
249
250
# File 'lib/tree_haver/backends/psych.rb', line 246

def initialize(node, source, lines = nil)
  @inner_node = node
  @source = source
  @lines = lines || source.lines
end

Instance Attribute Details

#inner_node::Psych::Nodes::Node (readonly)

Returns The underlying Psych node.

Returns:

  • (::Psych::Nodes::Node)

    The underlying Psych node



236
237
238
# File 'lib/tree_haver/backends/psych.rb', line 236

def inner_node
  @inner_node
end

#sourceString (readonly)

Returns The original source.

Returns:

  • (String)

    The original source



239
240
241
# File 'lib/tree_haver/backends/psych.rb', line 239

def source
  @source
end

Instance Method Details

#<=>(other) ⇒ Integer?

Comparison for sorting

Parameters:

  • other (Node)

    other node

Returns:

  • (Integer, nil)

    comparison result



437
438
439
440
441
442
# File 'lib/tree_haver/backends/psych.rb', line 437

def <=>(other)
  return unless other.respond_to?(:start_byte)
  cmp = start_byte <=> other.start_byte
  return cmp unless cmp&.zero?
  end_byte <=> other.end_byte
end

#alias?Boolean

Psych-specific: Check if this is an alias

Returns:

  • (Boolean)


494
495
496
# File 'lib/tree_haver/backends/psych.rb', line 494

def alias?
  @inner_node.is_a?(::Psych::Nodes::Alias)
end

#anchorString?

Psych-specific: Get the anchor name for Alias/anchored nodes

Returns:

  • (String, nil)

    Anchor name



452
453
454
# File 'lib/tree_haver/backends/psych.rb', line 452

def anchor
  @inner_node.anchor if @inner_node.respond_to?(:anchor)
end

#child(index) ⇒ Node?

Get child by index

Parameters:

  • index (Integer)

    Child index

Returns:

  • (Node, nil)

    Child node



317
318
319
# File 'lib/tree_haver/backends/psych.rb', line 317

def child(index)
  children[index]
end

#child_countInteger

Get the number of children

Returns:

  • (Integer)

    Child count



309
310
311
# File 'lib/tree_haver/backends/psych.rb', line 309

def child_count
  children.size
end

#childrenArray<Node>

Get child nodes

Returns:

  • (Array<Node>)

    Child nodes



291
292
293
294
295
# File 'lib/tree_haver/backends/psych.rb', line 291

def children
  return [] unless @inner_node.respond_to?(:children) && @inner_node.children

  @inner_node.children.map { |child| Node.new(child, @source, @lines) }
end

#each {|Node| ... } ⇒ Enumerator?

Iterate over child nodes

Yields:

  • (Node)

    Each child node

Returns:

  • (Enumerator, nil)


301
302
303
304
# File 'lib/tree_haver/backends/psych.rb', line 301

def each(&block)
  return to_enum(__method__) unless block
  children.each(&block)
end

#end_byteInteger

Get end byte offset

Returns:

  • (Integer)

    End byte offset



337
338
339
340
341
342
343
# File 'lib/tree_haver/backends/psych.rb', line 337

def end_byte
  return start_byte + text.bytesize unless @inner_node.respond_to?(:end_line)

  line = @inner_node.end_line || 0
  col = @inner_node.end_column || 0
  calculate_byte_offset(line, col)
end

#end_lineInteger

Get the 1-based line number where this node ends

Returns:

  • (Integer)

    1-based line number



376
377
378
379
# File 'lib/tree_haver/backends/psych.rb', line 376

def end_line
  row = end_point.row
  row + 1
end

#end_pointPoint

Get end point (row, column)

Returns:

  • (Point)

    End position (0-based)



357
358
359
360
361
# File 'lib/tree_haver/backends/psych.rb', line 357

def end_point
  row = (@inner_node.respond_to?(:end_line) ? @inner_node.end_line : 0) || 0
  col = (@inner_node.respond_to?(:end_column) ? @inner_node.end_column : 0) || 0
  Point.new(row, col)
end

#first_childNode?

Get the first child node

Returns:

  • (Node, nil)

    First child or nil



399
400
401
# File 'lib/tree_haver/backends/psych.rb', line 399

def first_child
  children.first
end

#has_error?Boolean

Check if the node or any descendant has an error

Psych raises on errors rather than embedding them.

Returns:

  • (Boolean)

    false



420
421
422
# File 'lib/tree_haver/backends/psych.rb', line 420

def has_error?
  false
end

#inspectString

Returns human-readable representation.

Returns:

  • (String)

    human-readable representation



445
446
447
# File 'lib/tree_haver/backends/psych.rb', line 445

def inspect
  "#<TreeHaver::Backends::Psych::Node type=#{type} children=#{child_count}>"
end

#mapping?Boolean

Psych-specific: Check if this is a mapping (hash)

Returns:

  • (Boolean)


473
474
475
# File 'lib/tree_haver/backends/psych.rb', line 473

def mapping?
  @inner_node.is_a?(::Psych::Nodes::Mapping)
end

#mapping_entriesArray<Array(Node, Node)>

Psych-specific: Get mapping entries as key-value pairs

For Mapping nodes, children alternate key, value, key, value…

Returns:

  • (Array<Array(Node, Node)>)

    Key-value pairs



503
504
505
506
507
508
509
510
511
# File 'lib/tree_haver/backends/psych.rb', line 503

def mapping_entries
  return [] unless mapping?

  pairs = []
  children.each_slice(2) do |key, val|
    pairs << [key, val] if key && val
  end
  pairs
end

#missing?Boolean

Check if this is a missing node

Psych doesn’t have missing nodes.

Returns:

  • (Boolean)

    false



429
430
431
# File 'lib/tree_haver/backends/psych.rb', line 429

def missing?
  false
end

#named?Boolean Also known as: structural?

Check if this is a named (structural) node

All Psych nodes are structural.

Returns:

  • (Boolean)

    true



408
409
410
# File 'lib/tree_haver/backends/psych.rb', line 408

def named?
  true
end

#scalar?Boolean

Psych-specific: Check if this is a scalar (primitive)

Returns:

  • (Boolean)


487
488
489
# File 'lib/tree_haver/backends/psych.rb', line 487

def scalar?
  @inner_node.is_a?(::Psych::Nodes::Scalar)
end

#sequence?Boolean

Psych-specific: Check if this is a sequence (array)

Returns:

  • (Boolean)


480
481
482
# File 'lib/tree_haver/backends/psych.rb', line 480

def sequence?
  @inner_node.is_a?(::Psych::Nodes::Sequence)
end

#source_positionHash{Symbol => Integer}

Get position information as a hash

Returns a hash with 1-based line numbers and 0-based columns. Compatible with *-merge gems’ FileAnalysisBase.

Returns:

  • (Hash{Symbol => Integer})

    Position hash



387
388
389
390
391
392
393
394
# File 'lib/tree_haver/backends/psych.rb', line 387

def source_position
  {
    start_line: start_line,
    end_line: end_line,
    start_column: start_point.column,
    end_column: end_point.column,
  }
end

#start_byteInteger

Get start byte offset

Psych doesn’t provide byte offsets directly, so we calculate from line/column.

Returns:

  • (Integer)

    Start byte offset



326
327
328
329
330
331
332
# File 'lib/tree_haver/backends/psych.rb', line 326

def start_byte
  return 0 unless @inner_node.respond_to?(:start_line)

  line = @inner_node.start_line || 0
  col = @inner_node.start_column || 0
  calculate_byte_offset(line, col)
end

#start_lineInteger

Get the 1-based line number where this node starts

Psych provides 0-based line numbers, so we add 1.

Returns:

  • (Integer)

    1-based line number



368
369
370
371
# File 'lib/tree_haver/backends/psych.rb', line 368

def start_line
  row = start_point.row
  row + 1
end

#start_pointPoint

Get start point (row, column)

Returns:

  • (Point)

    Start position (0-based)



348
349
350
351
352
# File 'lib/tree_haver/backends/psych.rb', line 348

def start_point
  row = (@inner_node.respond_to?(:start_line) ? @inner_node.start_line : 0) || 0
  col = (@inner_node.respond_to?(:start_column) ? @inner_node.start_column : 0) || 0
  Point.new(row, col)
end

#tagString?

Psych-specific: Get the tag for tagged nodes

Returns:

  • (String, nil)

    Tag



459
460
461
# File 'lib/tree_haver/backends/psych.rb', line 459

def tag
  @inner_node.tag if @inner_node.respond_to?(:tag)
end

#textString

Get the text content of this node

For Scalar nodes, returns the value. For containers, returns the source text spanning the node’s location.

Returns:

  • (String)

    Node text



276
277
278
279
280
281
282
283
284
285
286
# File 'lib/tree_haver/backends/psych.rb', line 276

def text
  case @inner_node
  when ::Psych::Nodes::Scalar
    @inner_node.value.to_s
  when ::Psych::Nodes::Alias
    "*#{@inner_node.anchor}"
  else
    # For container nodes, extract from source using location
    extract_text_from_location
  end
end

#typeString Also known as: kind

Get the node type as a string

Maps Psych class names to lowercase type strings:

  • Psych::Nodes::Stream → “stream”

  • Psych::Nodes::Document → “document”

  • Psych::Nodes::Mapping → “mapping”

  • Psych::Nodes::Sequence → “sequence”

  • Psych::Nodes::Scalar → “scalar”

  • Psych::Nodes::Alias → “alias”

Returns:

  • (String)

    Node type



263
264
265
# File 'lib/tree_haver/backends/psych.rb', line 263

def type
  @inner_node.class.name.split("::").last.downcase
end

#valueString?

Psych-specific: Get the scalar value

Returns:

  • (String, nil)

    Value for scalar nodes



466
467
468
# File 'lib/tree_haver/backends/psych.rb', line 466

def value
  @inner_node.value if @inner_node.respond_to?(:value)
end