Skip to content

Instantly share code, notes, and snippets.

@marzapower
Forked from lyoshenka/example.rb
Created November 30, 2021 20:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marzapower/5b030e036284243f8feb1c801440bc8d to your computer and use it in GitHub Desktop.
Save marzapower/5b030e036284243f8feb1c801440bc8d to your computer and use it in GitHub Desktop.
Example of outputting YAML in Ruby using inline style (array is all on one line, not spread out with each element on its own line)
full_data = {
response: {body: StyledYAML.literal(DATA.read), status: 200},
person: StyledYAML.inline('name' => 'Steve', 'age' => 24),
array: StyledYAML.inline(%w[ apples bananas oranges ])
}
StyledYAML.dump full_data, $stdout
__END__
{
"page": 1,
"results": [
"item", "another"
],
"total_pages": 0
}
# default boring output of Psych.dump
---
:response:
:body: ! "{\n \"page\": 1,\n \"results\": [\n \"item\", \"another\"\n ],\n
\ \"total_pages\": 0\n}\n"
:status: 200
:person:
name: Steve
age: 24
:array:
- apples
- bananas
- oranges
---
:response:
# formatted string presented in literal style
:body: |
{
"page": 1,
"results": [
"item", "another"
],
"total_pages": 0
}
:status: 200
# inline hash
:person: {name: Steve, age: 24}
# inline array
:array: [apples, bananas, oranges]
require 'psych'
require 'stringio'
# Public: A Psych extension to enable choosing output styles for specific
# objects.
#
# Thanks to Tenderlove for help in <http://stackoverflow.com/q/9640277/11687>
#
# Examples
#
# data = {
# response: { body: StyledYAML.literal(json_string), status: 200 },
# person: StyledYAML.inline({ 'name' => 'Stevie', 'age' => 12 }),
# array: StyledYAML.inline(%w[ apples bananas oranges ])
# }
#
# StyledYAML.dump data, $stdout
#
module StyledYAML
# Tag strings to be output using literal style
def self.literal obj
obj.extend LiteralScalar
return obj
end
# http://www.yaml.org/spec/1.2/spec.html#id2795688
module LiteralScalar
def yaml_style() Psych::Nodes::Scalar::LITERAL end
end
# Tag Hashes or Arrays to be output all on one line
def self.inline obj
case obj
when Hash then obj.extend FlowMapping
when Array then obj.extend FlowSequence
else
warn "#{self}: unrecognized type to inline (#{obj.class.name})"
end
return obj
end
# http://www.yaml.org/spec/1.2/spec.html#id2790832
module FlowMapping
def yaml_style() Psych::Nodes::Mapping::FLOW end
end
# http://www.yaml.org/spec/1.2/spec.html#id2790320
module FlowSequence
def yaml_style() Psych::Nodes::Sequence::FLOW end
end
# Custom tree builder class to recognize scalars tagged with `yaml_style`
class TreeBuilder < Psych::TreeBuilder
attr_writer :next_sequence_or_mapping_style
def initialize(*args)
super
@next_sequence_or_mapping_style = nil
end
def next_sequence_or_mapping_style default_style
style = @next_sequence_or_mapping_style || default_style
@next_sequence_or_mapping_style = nil
style
end
def scalar value, anchor, tag, plain, quoted, style
if style_any?(style) and value.respond_to?(:yaml_style) and style = value.yaml_style
if style_literal? style
plain = false
quoted = true
end
end
super
end
def style_any?(style) Psych::Nodes::Scalar::ANY == style end
def style_literal?(style) Psych::Nodes::Scalar::LITERAL == style end
%w[sequence mapping].each do |type|
class_eval <<-RUBY
def start_#{type}(anchor, tag, implicit, style)
style = next_sequence_or_mapping_style(style)
super
end
RUBY
end
end
# Custom tree class to handle Hashes and Arrays tagged with `yaml_style`
class YAMLTree < Psych::Visitors::YAMLTree
%w[Hash Array Psych_Set Psych_Omap].each do |klass|
class_eval <<-RUBY
def visit_#{klass} o
if o.respond_to? :yaml_style
@emitter.next_sequence_or_mapping_style = o.yaml_style
end
super
end
RUBY
end
end
# A Psych.dump alternative that uses the custom TreeBuilder
def self.dump obj, io = nil, options = {}
real_io = io || StringIO.new(''.encode('utf-8'))
visitor = YAMLTree.create(options, TreeBuilder.new)
visitor << obj
ast = visitor.tree
begin
ast.yaml real_io
rescue
# The `yaml` method was introduced in later versions, so fall back to
# constructing a visitor
Psych::Visitors::Emitter.new(real_io).accept ast
end
io ? io : real_io.string
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment