Skip to content

Instantly share code, notes, and snippets.

@jesseadams
Created April 22, 2019 21:01
Show Gist options
  • Save jesseadams/4cb0351761b6a77db76a22adc2312a75 to your computer and use it in GitHub Desktop.
Save jesseadams/4cb0351761b6a77db76a22adc2312a75 to your computer and use it in GitHub Desktop.
Psych monkey patch
# Psych's first step is to parse the Yaml into an AST of Node objects
# so we open the Node class and add a way to track the line.
class Psych::Nodes::Node
attr_accessor :line
end
# We need to provide a handler that will add the line to the node
# as it is parsed. TreeBuilder is the "usual" handler, that
# creates the AST.
class LineNumberHandler < Psych::TreeBuilder
# The handler needs access to the parser in order to call mark
attr_accessor :parser
# We are only interested in scalars, so here we override
# the method so that it calls mark and adds the line info
# to the node.
def scalar value, anchor, tag, plain, quoted, style
mark = parser.mark
s = super
s.line = mark.line
s
end
end
# The next step is to convert the AST to a Ruby object.
# Psych does this using the visitor pattern with the ToRuby
# visitor. Here we patch ToRuby rather than inherit from it
# as it makes the last step a little easier.
class Psych::Visitors::ToRuby
def revive_hash hash, o
o.children.each_slice(2) { |k,v|
key = accept(k)
val = accept(v)
# This is the important bit. If the value is a scalar,
# we replace it with the desired hash.
if v.is_a? ::Psych::Nodes::Scalar
val = { "value" => val, "line" => v.start_line + 1} # line is 0 based, so + 1
end
hash[key] = val
}
hash
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment