-
-
Save jamesramsay/d05d1dbbbebd7c80c5472419154d59f6 to your computer and use it in GitHub Desktop.
Revisions
-
jamesramsay revised this gist
Jul 17, 2020 . 1 changed file with 118 additions and 47 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,122 +1,193 @@ # The MIT License # # Copyright 2012 Mislav Marohnić <[email protected]>. # Copyright 2014 Jakub Jirutka <[email protected]>. # Copyright 2020 James Ramsay <[email protected]>. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. require 'psych' require 'stringio' # A Psych extension to enable choosing output styles for specific objects. # # Thanks to Tenderlove for help in <http://stackoverflow.com/q/9640277/11687>. # # Example: # # 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 # http://www.yaml.org/spec/1.2/spec.html#id2795688 module LiteralScalar def yaml_style Psych::Nodes::Scalar::LITERAL end end # https://yaml.org/spec/1.2/spec.html#id2788097 module SingleQuotedScalar def yaml_style Psych::Nodes::Scalar::SINGLE_QUOTED end end # https://yaml.org/spec/1.2/spec.html#id2787109 module DoubleQuotedScalar def yaml_style Psych::Nodes::Scalar::DOUBLE_QUOTED end end # http://www.yaml.org/spec/1.2/spec.html#id2796251 module FoldedScalar def yaml_style Psych::Nodes::Scalar::FOLDED end 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_seq_or_map_style def next_seq_or_map_style(default_style) style = @next_seq_or_map_style || default_style @next_seq_or_map_style = nil style end def scalar(value, anchor, tag, plain, quoted, style) if value.respond_to?(:yaml_style) if style_literal_or_folded? value.yaml_style plain = false quoted = true end style = value.yaml_style end super end def style_literal_or_folded?(style) [Psych::Nodes::Scalar::LITERAL, Psych::Nodes::Scalar::FOLDED].include?(style) end [:sequence, :mapping].each do |type| class_eval <<-RUBY def start_#{type}(anchor, tag, implicit, style) style = next_seq_or_map_style(style) super end RUBY end end # Custom tree class to handle Hashes and Arrays tagged with `yaml_style`. class YAMLTree < Psych::Visitors::YAMLTree [:Hash, :Array, :Psych_Set, :Psych_Omap].each do |klass| class_eval <<-RUBY def visit_#{klass}(o) if o.respond_to? :yaml_style @emitter.next_seq_or_map_style = o.yaml_style end super end RUBY end end # Tag string to be output using literal style. def self.literal(str) str.extend(LiteralScalar) str end # Tag string to be output using folded style. def self.folded(str) str.extend(FoldedScalar) str end # Tag string to be output using single quoted style. def self.single_quoted(str) str.extend(SingleQuotedScalar) str end # Tag string to be output using double quoted style. def self.double_quoted(str) str.extend(DoubleQuotedScalar) str end # Tag Hash or Array to be output all on one line. def self.inline(obj) case obj when Hash obj.extend(FlowMapping) when Array obj.extend(FlowSequence) else warn "#{self}: unrecognized type to inline (#{obj.class.name})" end obj 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 || real_io.string end end -
mislav revised this gist
Mar 12, 2012 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,6 +4,8 @@ # 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 = { -
mislav revised this gist
Mar 12, 2012 . 3 changed files with 29 additions and 0 deletions.There are no files selected for viewing
File renamed without changes.This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,13 @@ # 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 This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,16 @@ --- :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] -
mislav revised this gist
Mar 12, 2012 . 3 changed files with 136 additions and 57 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,16 @@ 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 } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,120 @@ require 'psych' require 'stringio' # Public: A Psych extension to enable choosing output styles for specific # objects. # # 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.new(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 This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,57 +0,0 @@ -
mislav created this gist
Mar 12, 2012 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,57 @@ require 'psych' require 'stringio' # Extend strings with this module to mark them to be output in YAML using # literal style. module LiteralStyleYaml def literal_style?() true end # Custom tree builder class to recognize scalars tagged with `literal_style?` class TreeBuilder < Psych::TreeBuilder def scalar value, anchor, tag, plain, quoted, style if value.respond_to? :literal_style? and value.literal_style? and style == Psych::Nodes::Scalar::ANY plain = false quoted = true style = Psych::Nodes::Scalar::LITERAL end super 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 = Psych::Visitors::YAMLTree.new(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 ## EXAMPLE USAGE json = DATA.read json.extend LiteralStyleYaml full_data = {response: {body: json, status: 200} } LiteralStyleYaml.dump full_data, $stdout __END__ { "page": 1, "results": [ "item", "another" ], "total_pages": 0 }