Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/esotericpig/psychgus
:necktie::blue_car::nose: Easily style YAML files using Psych, like Sequence/Mapping Flow style.
https://github.com/esotericpig/psychgus
libyaml psych psych-parser psychgus ruby style yaml yaml-parser
Last synced: about 2 months ago
JSON representation
:necktie::blue_car::nose: Easily style YAML files using Psych, like Sequence/Mapping Flow style.
- Host: GitHub
- URL: https://github.com/esotericpig/psychgus
- Owner: esotericpig
- License: lgpl-3.0
- Created: 2017-12-31T04:30:50.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2022-08-01T02:08:37.000Z (over 2 years ago)
- Last Synced: 2024-04-25T17:41:26.616Z (8 months ago)
- Topics: libyaml, psych, psych-parser, psychgus, ruby, style, yaml, yaml-parser
- Language: Ruby
- Homepage:
- Size: 167 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# Psychgus
[![Gem Version](https://badge.fury.io/rb/psychgus.svg)](https://badge.fury.io/rb/psychgus)
[![CI Status](https://github.com/esotericpig/psychgus/actions/workflows/ci.yml/badge.svg)](https://github.com/esotericpig/psychgus/actions/workflows/ci.yml)
[![Doc Coverage](http://inch-ci.org/github/esotericpig/psychgus.svg?branch=master)](https://inch-ci.org/github/esotericpig/psychgus)[![Documentation](https://img.shields.io/badge/doc-yard-%23A0522D.svg)](https://esotericpig.github.io/docs/psychgus/yardoc/index.html)
[![Source Code](https://img.shields.io/badge/source-github-%23211F1F.svg)](https://github.com/esotericpig/psychgus)
[![Changelog](https://img.shields.io/badge/changelog-md-%23A0522D.svg)](CHANGELOG.md)
[![License](https://img.shields.io/github/license/esotericpig/psychgus.svg)](LICENSE.txt)Psychgus uses the core standard library [Psych](https://github.com/ruby/psych) for working with [YAML](https://yaml.org) and extends it so that developers can easily style the YAML according to their needs.
Turn this YAML...
```YAML
---
Psych Gus:
Aliases:
- Longbranch Pennywhistle
- Squirts Macintosh
- Clementine Woolysocks
- Lavender Gooms
- Big Baby Burton
- Chocolate Einstein
- MC Clap Yo Handz
Skills:
- The Blueberry
- The Super Sniffer
- Positive Work Attitude
```Into this:
```YAML
---
Psych Gus:
Aliases: [Longbranch Pennywhistle, Squirts Macintosh, Clementine Woolysocks, Lavender
Gooms, Big Baby Burton, Chocolate Einstein, MC Clap Yo Handz]
Skills: [The Blueberry, The Super Sniffer, Positive Work Attitude]
```Thank you to the people that work hard on the Psych project.
The Psychgus name comes from the well-styled character Gus from the TV show Psych.
## Contents
- [Setup](#setup)
- [Using](#using)
- [Simple Example](#simple-example)
- [Hash Example](#hash-example)
- [Class Example](#class-example)
- [Advanced Usage](#advanced-usage)
- [Common Stylers](#common-stylers)
- [Stylers Example](#stylers-example)
- [Hacking](#hacking)
- [Testing](#testing)
- [Generating Doc](#generating-doc)
- [License](#license)## Setup
Pick your poison...
With the RubyGems CLI package manager:
`$ gem install psychgus`
In your *Gemspec* (*<project>.gemspec*):
```Ruby
# Pick one...
spec.add_dependency 'psychgus', '~> X.X.X'
spec.add_development_dependency 'psychgus', '~> X.X.X'
```In your *Gemfile*:
```Ruby
# Pick one...
gem 'psychgus', '~> X.X.X'
gem 'psychgus', '~> X.X.X', :group => :development
gem 'psychgus', :git => 'https://github.com/esotericpig/psychgus.git', :tag => 'vX.X.X'
```Manually:
```
$ git clone 'https://github.com/esotericpig/psychgus.git'
$ cd psychgus
$ bundle install
$ bundle exec rake install:local
```## Using
Documentation (YARDoc) is available on my [GitHub Page](https://esotericpig.github.io/docs/psychgus/yardoc/index.html) and on [RubyDoc.info](https://www.rubydoc.info/gems/psychgus).
To begin styling, create a class and mix in (include) `Psychgus::Styler`. Then pass it in as a keyword arg (`stylers: MyStyler.new` or `stylers: [MyStyler1.new,MyStyler2.new]`) into one of the Psychgus methods.
For classes, you can optionally include `Psychgus::Blueberry` and return the styler(s) for the class by defining the `psychgus_stylers(sniffer)` method.
Instead of making your own styler, you can also use one of the [pre-defined stylers](#common-stylers).
### Contents | Using
[Simple Example](#simple-example)
| [Hash Example](#hash-example)
| [Class Example](#class-example)
| [Advanced Usage](#advanced-usage)
| [Common Stylers](#common-stylers)### Simple Example
```Ruby
require 'psychgus'class CoffeeStyler
include Psychgus::Stylerdef style_sequence(sniffer,node)
node.style = Psychgus::SEQUENCE_FLOW
end
endcoffee = {
'Roast'=>['Light', 'Medium', 'Dark', 'Extra Dark'],
'Style'=>['Cappuccino', 'Espresso', 'Latte', 'Mocha']
}puts coffee.to_yaml(stylers: CoffeeStyler.new)
# Output:
# ---
# Roast: [Light, Medium, Dark, Extra Dark]
# Style: [Cappuccino, Espresso, Latte, Mocha]class Coffee
include Psychgus::Blueberrydef initialize
@roast = ['Light', 'Medium', 'Dark', 'Extra Dark']
@style = ['Cappuccino', 'Espresso', 'Latte', 'Mocha']
enddef psychgus_stylers(sniffer)
CoffeeStyler.new
end
endputs Coffee.new.to_yaml
# Output:
# --- !ruby/object:Coffee
# roast: [Light, Medium, Dark, Extra Dark]
# style: [Cappuccino, Espresso, Latte, Mocha]
```### Hash Example
```Ruby
require 'psychgus'class BurgerStyler
include Psychgus::Styler # Mix in methods needed for styling# Style maps (Psych::Nodes::Mapping)
# - Hashes (key/value pairs)
# - Example: "Burgers: Classic {}"
def style_mapping(sniffer,node)
node.style = Psychgus::MAPPING_FLOW if sniffer.level >= 4
end# Style scalars (Psych::Nodes::Scalar)
# - Any text (non-alias)
def style_scalar(sniffer,node)
# Remove colon (change symbols into strings)
node.value = node.value.sub(':','')# Capitalize each word
node.value = node.value.split(' ').map do |v|
if v.casecmp('BBQ') == 0
v.upcase()
else
v.capitalize()
end
end.join(' ')# Change lettuce to spinach
node.value = 'Spinach' if node.value == 'Lettuce'
end# Style sequences (Psych::Nodes::Sequence)
# - Arrays
# - Example: "[Lettuce, Onions, Pickles, Tomatoes]"
def style_sequence(sniffer,node)
node.style = Psychgus::SEQUENCE_FLOW if sniffer.level >= 4
end
endburgers = {
:burgers => {
:classic => {:sauce => %w(Ketchup Mustard),
:cheese => 'American',
:bun => 'Sesame Seed'},
:bbq => {:sauce => 'Honey BBQ',
:cheese => 'Cheddar',
:bun => 'Kaiser'},
:fancy => {:sauce => 'Spicy Wasabi',
:cheese => 'Smoked Gouda',
:bun => 'Hawaiian'}
},
:toppings => [
'Mushrooms',
%w(Lettuce Onions Pickles Tomatoes),
[%w(Ketchup Mustard), %w(Salt Pepper)]
]
}
burgers[:favorite] = burgers[:burgers][:bbq] # Aliasputs burgers.to_yaml(indent: 3,stylers: BurgerStyler.new)
# Output:
# ---
# Burgers:
# Classic: {Sauce: [Ketchup, Mustard], Cheese: American, Bun: Sesame Seed}
# BBQ: &1 {Sauce: Honey BBQ, Cheese: Cheddar, Bun: Kaiser}
# Fancy: {Sauce: Spicy Wasabi, Cheese: Smoked Gouda, Bun: Hawaiian}
# Toppings:
# - Mushrooms
# - [Spinach, Onions, Pickles, Tomatoes]
# - [[Ketchup, Mustard], [Salt, Pepper]]
# Favorite: *1# Or pass in a Hash. Can also dereference aliases.
puts burgers.to_yaml({:indent => 3,:stylers => BurgerStyler.new,:deref_aliases => true})# Output:
# ---
# Burgers:
# Classic: {Sauce: [Ketchup, Mustard], Cheese: American, Bun: Sesame Seed}
# BBQ: {Sauce: Honey BBQ, Cheese: Cheddar, Bun: Kaiser}
# Fancy: {Sauce: Spicy Wasabi, Cheese: Smoked Gouda, Bun: Hawaiian}
# Toppings:
# - Mushrooms
# - [Spinach, Onions, Pickles, Tomatoes]
# - [[Ketchup, Mustard], [Salt, Pepper]]
# Favorite:
# Sauce: Honey BBQ
# Cheese: Cheddar
# Bun: Kaiser
```### Class Example
```Ruby
require 'psychgus'class BurgerStyler
include Psychgus::Styler # Mix in methods needed for stylingdef initialize(sniffer)
@class_level = sniffer.level
@class_position = sniffer.position
end# Style all nodes (Psych::Nodes::Node)
def style(sniffer,node)
# Remove "!ruby/object:..." for Burger classes (not Burgers class)
node.tag = nil if node.node_of?(:mapping,:scalar,:sequence)# This is another way to do the above
#node.tag = nil if node.respond_to?(:tag=)
end# Style maps (Psych::Nodes::Mapping)
# - Hashes (key/value pairs)
# - Example: "Burgers: Classic {}"
def style_mapping(sniffer,node)
parent = sniffer.parentif !parent.nil?()
# BBQ
node.style = Psychgus::MAPPING_FLOW if parent.node_of?(:scalar) &&
parent.value.casecmp('BBQ') == 0
end
end# Style scalars (Psych::Nodes::Scalar)
# - Any text (non-alias)
def style_scalar(sniffer,node)
parent = sniffer.parent# Single quote scalars that are not keys to a map
# - "child_key?" is the same as "child_type == :key"
node.style = Psychgus::SCALAR_SINGLE_QUOTED unless parent.child_key?()
end# Style sequences (Psych::Nodes::Sequence)
# - Arrays
# - Example: "[Lettuce, Onions, Pickles, Tomatoes]"
def style_sequence(sniffer,node)
relative_level = (sniffer.level - @class_level) + 1# "[Ketchup, Mustard]"
node.style = Psychgus::SEQUENCE_FLOW if relative_level == 3
end
endclass Burger
include Psychgus::Blueberry # Mix in methods needed to be stylableattr_accessor :bun
attr_accessor :cheese
attr_accessor :saucedef initialize(sauce,cheese,bun)
@bun = bun
@cheese = cheese
@sauce = sauce
end# Return our styler(s)
# - Can be an Array: [MyStyler1.new, MyStyler2.new]
def psychgus_stylers(sniffer)
return BurgerStyler.new(sniffer)
end# You can still use Psych's encode_with(), no problem
def encode_with(coder)
coder['Bun'] = @bun
coder['Cheese'] = @cheese
coder['Sauce'] = @sauce
end
endclass Burgers
attr_accessor :burgers
attr_accessor :toppings
attr_accessor :favoritedef initialize()
@burgers = {
'Classic' => Burger.new(['Ketchup','Mustard'],'American' ,'Sesame Seed'),
'BBQ' => Burger.new('Honey BBQ' ,'Cheddar' ,'Kaiser'),
'Fancy' => Burger.new('Spicy Wasabi' ,'Smoked Gouda','Hawaiian')
}@toppings = [
'Mushrooms',
%w(Lettuce Onions Pickles Tomatoes),
[%w(Ketchup Mustard),%w(Salt Pepper)]
]@favorite = @burgers['BBQ'] # Alias
end# You can still use Psych's encode_with(), no problem
def encode_with(coder)
coder['Burgers'] = @burgers
coder['Toppings'] = @toppings
coder['Favorite'] = @favorite
end
endburgers = Burgers.new
puts burgers.to_yaml(indent: 3)
# Output:
# --- !ruby/object:Burgers
# Burgers:
# Classic:
# Bun: 'Sesame Seed'
# Cheese: 'American'
# Sauce: ['Ketchup', 'Mustard']
# BBQ: &1 {Bun: 'Kaiser', Cheese: 'Cheddar', Sauce: 'Honey BBQ'}
# Fancy:
# Bun: 'Hawaiian'
# Cheese: 'Smoked Gouda'
# Sauce: 'Spicy Wasabi'
# Toppings:
# - Mushrooms
# - - Lettuce
# - Onions
# - Pickles
# - Tomatoes
# - - - Ketchup
# - Mustard
# - - Salt
# - Pepper
# Favorite: *1# Or pass in a Hash. Can also dereference aliases.
puts burgers.to_yaml({:indent => 3,:deref_aliases => true})# Output:
# --- !ruby/object:Burgers
# Burgers:
# Classic:
# Bun: 'Sesame Seed'
# Cheese: 'American'
# Sauce: ['Ketchup', 'Mustard']
# BBQ: {Bun: 'Kaiser', Cheese: 'Cheddar', Sauce: 'Honey BBQ'}
# Fancy:
# Bun: 'Hawaiian'
# Cheese: 'Smoked Gouda'
# Sauce: 'Spicy Wasabi'
# Toppings:
# - Mushrooms
# - - Lettuce
# - Onions
# - Pickles
# - Tomatoes
# - - - Ketchup
# - Mustard
# - - Salt
# - Pepper
# Favorite:
# Bun: 'Kaiser'
# Cheese: 'Cheddar'
# Sauce: 'Honey BBQ'
```### Advanced Usage
```Ruby
require 'psychgus'class MyStyler
include Psychgus::Stylerdef style_sequence(sniffer,node)
node.style = Psychgus::SEQUENCE_FLOW
end
endcoffee = {
'Coffee' => {
'Roast'=>['Light', 'Medium', 'Dark', 'Extra Dark'],
'Style'=>['Cappuccino', 'Espresso', 'Latte', 'Mocha']
}
}
eggs = {
'Eggs' => {
'Color'=>['Brown', 'White', 'Blue', 'Olive'],
'Style'=>['Fried', 'Scrambled', 'Omelette', 'Poached']
}
}filename = 'coffee-and-eggs.yaml'
styler = MyStyler.new
options = {:indentation=>3, :stylers=>styler, :deref_aliases=>true}coffee_yaml = coffee.to_yaml(options)
coffee_and_eggs_yaml = Psychgus.dump_stream(coffee,eggs,options)# High-level emitting
puts Psychgus.dump(coffee,options)
putsPsychgus.dump_file(filename,coffee,eggs,options)
puts File.readlines(filename)
putsputs Psychgus.dump_stream(coffee,eggs,options)
putsputs coffee.to_yaml(options)
puts# High-level parsing
# - Because to_ruby() will be called, just use Psych:
# - load(), load_file(), load_stream(), safe_load()# Mid-level emitting
stream = Psychgus.parse_stream(coffee_and_eggs_yaml,options)puts stream.to_yaml()
puts# Mid-level parsing
puts Psychgus.parse(coffee_yaml,options).to_ruby
putsputs Psychgus.parse_file(filename,options).to_ruby
putsi = 0
Psychgus.parse_stream(coffee_and_eggs_yaml,options) do |doc|
puts "Doc ##{i += 1}:"
puts " #{doc.to_ruby}"
end
puts# Low-level emitting
tree_builder = Psychgus::StyledTreeBuilder.new(styler,options)
visitor = Psych::Visitors::YAMLTree.create(options,tree_builder)visitor << coffee
visitor << eggsputs visitor.tree.to_yaml
puts# Low-level parsing
parser = Psychgus.parser(options)parser.parse(coffee_yaml)
puts parser.handler.root.to_ruby
puts
```### Common Stylers
A collection of commonly-used [Stylers](https://esotericpig.github.io/docs/psychgus/yardoc/Psychgus/Stylers.html) and [Stylables](https://esotericpig.github.io/docs/psychgus/yardoc/Psychgus/Stylables.html) are included with Psychgus.
| Styler | Description |
| --- | --- |
| [CapStyler](https://esotericpig.github.io/docs/psychgus/yardoc/Psychgus/Stylers/CapStyler.html) | Capitalizer for Scalars |
| [FlowStyler](https://esotericpig.github.io/docs/psychgus/yardoc/Psychgus/Stylers/FlowStyler.html) | FLOW style changer for Mappings & Sequences |
| [MapFlowStyler](https://esotericpig.github.io/docs/psychgus/yardoc/Psychgus/Stylers/MapFlowStyler.html) | FLOW style changer for Mappings only |
| [NoSymStyler](https://esotericpig.github.io/docs/psychgus/yardoc/Psychgus/Stylers/NoSymStyler.html) | Symbol remover for Scalars |
| [NoTagStyler](https://esotericpig.github.io/docs/psychgus/yardoc/Psychgus/Stylers/NoTagStyler.html) | Tag remover for classes |
| [SeqFlowStyler](https://esotericpig.github.io/docs/psychgus/yardoc/Psychgus/Stylers/SeqFlowStyler.html) | FLOW style changer for Sequences only |#### Stylers Example
```Ruby
require 'psychgus'class EggCarton
def initialize
@eggs = {
:styles => ['fried', 'scrambled', ['BBQ', 'ketchup & mustard']],
:colors => ['brown', 'white', ['blue', 'green']]
}
end
endputs EggCarton.new.to_yaml(stylers: [
Psychgus::NoSymStyler.new,
Psychgus::NoTagStyler.new,
Psychgus::CapStyler.new,
Psychgus::FlowStyler.new(4)
])# Output:
# ---
# Eggs:
# Styles: [Fried, Scrambled, [BBQ, Ketchup & Mustard]]
# Colors: [Brown, White, [Blue, Green]]puts EggCarton.new.to_yaml
# Output (without Stylers):
# --- !ruby/object:EggCarton
# eggs:
# :styles:
# - fried
# - scrambled
# - - BBQ
# - ketchup & mustard
# :colors:
# - brown
# - white
# - - blue
# - green
```## Hacking
```
$ git clone 'https://github.com/esotericpig/psychgus.git'
$ cd psychgus
$ bundle install
$ bundle exec rake -T
```### Testing
Run tests:
`$ bundle exec rake test`
### Generating Doc
Generate doc:
`$ bundle exec rake doc`
Clean & generate pristine doc:
`$ bundle exec rake clobber doc`
## License
[GNU LGPL v3+](LICENSE.txt)
> Psychgus ()
> Copyright (c) 2017-2024 Bradley Whited
>
> Psychgus is free software: you can redistribute it and/or modify
> it under the terms of the GNU Lesser General Public License as published by
> the Free Software Foundation, either version 3 of the License, or
> (at your option) any later version.
>
> Psychgus is distributed in the hope that it will be useful,
> but WITHOUT ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> GNU Lesser General Public License for more details.
>
> You should have received a copy of the GNU Lesser General Public License
> along with Psychgus. If not, see .