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

Inherits:
Object
  • Object
show all
Includes:
Comparable, Enumerable
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



271
272
273
274
275
# File 'lib/tree_haver/backends/psych.rb', line 271

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



261
262
263
# File 'lib/tree_haver/backends/psych.rb', line 261

def inner_node
  @inner_node
end

#sourceString (readonly)

Returns The original source.

Returns:

  • (String)

    The original source



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

def source
  @source
end

Instance Method Details

#<=>(other) ⇒ Integer?

Comparison for sorting

Parameters:

  • other (Node)

    other node

Returns:

  • (Integer, nil)

    comparison result



462
463
464
465
466
467
# File 'lib/tree_haver/backends/psych.rb', line 462

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)


543
544
545
# File 'lib/tree_haver/backends/psych.rb', line 543

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



501
502
503
# File 'lib/tree_haver/backends/psych.rb', line 501

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



342
343
344
# File 'lib/tree_haver/backends/psych.rb', line 342

def child(index)
  children[index]
end

#child_countInteger

Get the number of children

Returns:

  • (Integer)

    Child count



334
335
336
# File 'lib/tree_haver/backends/psych.rb', line 334

def child_count
  children.size
end

#childrenArray<Node>

Get child nodes

Returns:

  • (Array<Node>)

    Child nodes



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

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)


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

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

#end_byteInteger

Get end byte offset

Returns:

  • (Integer)

    End byte offset



362
363
364
365
366
367
368
# File 'lib/tree_haver/backends/psych.rb', line 362

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



401
402
403
404
# File 'lib/tree_haver/backends/psych.rb', line 401

def end_line
  row = end_point.row
  row + 1
end

#end_pointPoint

Get end point (row, column)

Returns:

  • (Point)

    End position (0-based)



382
383
384
385
386
# File 'lib/tree_haver/backends/psych.rb', line 382

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



424
425
426
# File 'lib/tree_haver/backends/psych.rb', line 424

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



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

def has_error?
  false
end

#inspectString

Returns human-readable representation.

Returns:

  • (String)

    human-readable representation



470
471
472
# File 'lib/tree_haver/backends/psych.rb', line 470

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)


522
523
524
# File 'lib/tree_haver/backends/psych.rb', line 522

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



552
553
554
555
556
557
558
559
560
# File 'lib/tree_haver/backends/psych.rb', line 552

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



454
455
456
# File 'lib/tree_haver/backends/psych.rb', line 454

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



433
434
435
# File 'lib/tree_haver/backends/psych.rb', line 433

def named?
  true
end

#next_siblingvoid

This method returns an undefined value.

Get the next sibling

Raises:

  • (NotImplementedError)

    Psych nodes don’t have sibling references



478
479
480
# File 'lib/tree_haver/backends/psych.rb', line 478

def next_sibling
  raise NotImplementedError, "Psych backend does not support sibling navigation"
end

#parentvoid

This method returns an undefined value.

Get the parent node

Raises:

  • (NotImplementedError)

    Psych nodes don’t have parent references



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

def parent
  raise NotImplementedError, "Psych backend does not support parent navigation"
end

#prev_siblingvoid

This method returns an undefined value.

Get the previous sibling

Raises:

  • (NotImplementedError)

    Psych nodes don’t have sibling references



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

def prev_sibling
  raise NotImplementedError, "Psych backend does not support sibling navigation"
end

#scalar?Boolean

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

Returns:

  • (Boolean)


536
537
538
# File 'lib/tree_haver/backends/psych.rb', line 536

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

#sequence?Boolean

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

Returns:

  • (Boolean)


529
530
531
# File 'lib/tree_haver/backends/psych.rb', line 529

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



412
413
414
415
416
417
418
419
# File 'lib/tree_haver/backends/psych.rb', line 412

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



351
352
353
354
355
356
357
# File 'lib/tree_haver/backends/psych.rb', line 351

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



393
394
395
396
# File 'lib/tree_haver/backends/psych.rb', line 393

def start_line
  row = start_point.row
  row + 1
end

#start_pointPoint

Get start point (row, column)

Returns:

  • (Point)

    Start position (0-based)



373
374
375
376
377
# File 'lib/tree_haver/backends/psych.rb', line 373

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



508
509
510
# File 'lib/tree_haver/backends/psych.rb', line 508

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



301
302
303
304
305
306
307
308
309
310
311
# File 'lib/tree_haver/backends/psych.rb', line 301

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



288
289
290
# File 'lib/tree_haver/backends/psych.rb', line 288

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

#valueString?

Psych-specific: Get the scalar value

Returns:

  • (String, nil)

    Value for scalar nodes



515
516
517
# File 'lib/tree_haver/backends/psych.rb', line 515

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