psychgus 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +25 -0
- data/LICENSE.txt +165 -0
- data/README.md +507 -0
- data/Rakefile +158 -0
- data/lib/psychgus/blueberry.rb +110 -0
- data/lib/psychgus/ext/core_ext.rb +77 -0
- data/lib/psychgus/ext/node_ext.rb +68 -0
- data/lib/psychgus/ext/yaml_tree_ext.rb +101 -0
- data/lib/psychgus/ext.rb +32 -0
- data/lib/psychgus/styled_document_stream.rb +66 -0
- data/lib/psychgus/styled_tree_builder.rb +294 -0
- data/lib/psychgus/styler.rb +127 -0
- data/lib/psychgus/super_sniffer/parent.rb +218 -0
- data/lib/psychgus/super_sniffer.rb +420 -0
- data/lib/psychgus/version.rb +27 -0
- data/lib/psychgus.rb +693 -0
- data/psychgus.gemspec +121 -0
- data/test/blueberry_test.rb +141 -0
- data/test/psychgus_test.rb +142 -0
- data/test/psychgus_tester.rb +90 -0
- data/test/sniffer_test.rb +287 -0
- data/test/styler_test.rb +85 -0
- data/yard/templates/default/layout/html/footer.erb +5 -0
- metadata +182 -0
data/lib/psychgus.rb
ADDED
@@ -0,0 +1,693 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
#--
|
5
|
+
# This file is part of Psychgus.
|
6
|
+
# Copyright (c) 2017-2019 Jonathan Bradley Whited (@esotericpig)
|
7
|
+
#
|
8
|
+
# Psychgus is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
10
|
+
# the Free Software Foundation, either version 3 of the License, or
|
11
|
+
# (at your option) any later version.
|
12
|
+
#
|
13
|
+
# Psychgus is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Lesser General Public License for more details.
|
17
|
+
#
|
18
|
+
# You should have received a copy of the GNU Lesser General Public License
|
19
|
+
# along with Psychgus. If not, see <http://www.gnu.org/licenses/>.
|
20
|
+
#++
|
21
|
+
|
22
|
+
|
23
|
+
require 'psych'
|
24
|
+
|
25
|
+
require 'psychgus/blueberry'
|
26
|
+
require 'psychgus/ext'
|
27
|
+
require 'psychgus/styled_document_stream'
|
28
|
+
require 'psychgus/styled_tree_builder'
|
29
|
+
require 'psychgus/styler'
|
30
|
+
require 'psychgus/super_sniffer'
|
31
|
+
require 'psychgus/version'
|
32
|
+
|
33
|
+
require 'psychgus/ext/core_ext'
|
34
|
+
require 'psychgus/ext/node_ext'
|
35
|
+
require 'psychgus/ext/yaml_tree_ext'
|
36
|
+
|
37
|
+
require 'psychgus/super_sniffer/parent'
|
38
|
+
|
39
|
+
###
|
40
|
+
# Psychgus uses the core standard library {https://github.com/ruby/psych Psych} for working with YAML
|
41
|
+
# and extends it so that developers can easily style the YAML according to their needs.
|
42
|
+
# Thank you to the people that worked and continue to work hard on that project.
|
43
|
+
#
|
44
|
+
# The name comes from the well-styled character Gus from the TV show Psych.
|
45
|
+
#
|
46
|
+
# == Create a Styler
|
47
|
+
#
|
48
|
+
# First, we will create a {Styler}.
|
49
|
+
#
|
50
|
+
# All you need to do is add +include Psychgus::Styler+ to a class.
|
51
|
+
#
|
52
|
+
# Here is a complex {Styler} for the examples below:
|
53
|
+
# require 'psychgus'
|
54
|
+
#
|
55
|
+
# class BurgerStyler
|
56
|
+
# # Mix in methods needed for styling
|
57
|
+
# include Psychgus::Styler
|
58
|
+
#
|
59
|
+
# def initialize(sniffer=nil)
|
60
|
+
# if sniffer.nil?()
|
61
|
+
# @class_level = 0
|
62
|
+
# @class_position = 0
|
63
|
+
# else
|
64
|
+
# # For the Class Example
|
65
|
+
# @class_level = sniffer.level
|
66
|
+
# @class_position = sniffer.position
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# # Style all nodes (Psych::Nodes::Node)
|
71
|
+
# def style(sniffer,node)
|
72
|
+
# # Remove "!ruby/object:..." for classes
|
73
|
+
# node.tag = nil if node.node_of?(:mapping,:scalar,:sequence)
|
74
|
+
#
|
75
|
+
# # This is another way to do the above
|
76
|
+
# #node.tag = nil if node.respond_to?(:tag=)
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# # Style aliases (Psych::Nodes::Alias)
|
80
|
+
# def style_alias(sniffer,node)
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# # Style maps (Psych::Nodes::Mapping)
|
84
|
+
# # - Hashes (key/value pairs)
|
85
|
+
# # - Example: "Burgers: Classic {}"
|
86
|
+
# def style_mapping(sniffer,node)
|
87
|
+
# parent = sniffer.parent
|
88
|
+
#
|
89
|
+
# if !parent.nil?()
|
90
|
+
# # BBQ
|
91
|
+
# node.style = Psychgus::MAPPING_FLOW if parent.node_of?(:scalar) &&
|
92
|
+
# parent.value.casecmp('BBQ') == 0
|
93
|
+
# end
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# # Style scalars (Psych::Nodes::Scalar)
|
97
|
+
# # - Any text (non-alias)
|
98
|
+
# def style_scalar(sniffer,node)
|
99
|
+
# parent = sniffer.parent
|
100
|
+
#
|
101
|
+
# # Single quote scalars that are not keys to a map
|
102
|
+
# node.style = Psychgus::SCALAR_SINGLE_QUOTED if !parent.nil?() &&
|
103
|
+
# parent.child_type != :key
|
104
|
+
#
|
105
|
+
# # Remove colon (change symbols into strings)
|
106
|
+
# node.value = node.value.sub(':','')
|
107
|
+
#
|
108
|
+
# # Change lettuce to spinach
|
109
|
+
# node.value = 'Spinach' if node.value.casecmp('Lettuce') == 0
|
110
|
+
#
|
111
|
+
# # Capitalize each word
|
112
|
+
# node.value = node.value.split(' ').map do |v|
|
113
|
+
# if v.casecmp('BBQ') == 0
|
114
|
+
# v.upcase()
|
115
|
+
# else
|
116
|
+
# v.capitalize()
|
117
|
+
# end
|
118
|
+
# end.join(' ')
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
# # Style sequences (Psych::Nodes::Sequence)
|
122
|
+
# # - Arrays
|
123
|
+
# # - Example: "[Lettuce, Onions, Pickles, Tomatoes]"
|
124
|
+
# def style_sequence(sniffer,node)
|
125
|
+
# relative_level = (sniffer.level - @class_level) + 1
|
126
|
+
#
|
127
|
+
# node.style = Psychgus::SEQUENCE_FLOW if sniffer.level >= 4
|
128
|
+
#
|
129
|
+
# # Make "[Ketchup, Mustard]" a block for the Class Example
|
130
|
+
# node.style = Psychgus::SEQUENCE_BLOCK if relative_level == 7
|
131
|
+
# end
|
132
|
+
# end
|
133
|
+
#
|
134
|
+
# @example Hash example
|
135
|
+
# require 'psychgus'
|
136
|
+
#
|
137
|
+
# burgers = {
|
138
|
+
# :Burgers => {
|
139
|
+
# :Classic => {
|
140
|
+
# :Sauce => %w(Ketchup Mustard),
|
141
|
+
# :Cheese => 'American',
|
142
|
+
# :Bun => 'Sesame Seed'
|
143
|
+
# },
|
144
|
+
# :BBQ => {
|
145
|
+
# :Sauce => 'Honey BBQ',
|
146
|
+
# :Cheese => 'Cheddar',
|
147
|
+
# :Bun => 'Kaiser'
|
148
|
+
# },
|
149
|
+
# :Fancy => {
|
150
|
+
# :Sauce => 'Spicy Wasabi',
|
151
|
+
# :Cheese => 'Smoked Gouda',
|
152
|
+
# :Bun => 'Hawaiian'
|
153
|
+
# }
|
154
|
+
# },
|
155
|
+
# :Toppings => [
|
156
|
+
# 'Mushrooms',
|
157
|
+
# %w(Lettuce Onions Pickles Tomatoes),
|
158
|
+
# [%w(Ketchup Mustard), %w(Salt Pepper)]
|
159
|
+
# ]
|
160
|
+
# }
|
161
|
+
# burgers[:Favorite] = burgers[:Burgers][:BBQ] # Alias
|
162
|
+
#
|
163
|
+
# puts burgers.to_yaml(indent: 3,stylers: BurgerStyler.new,deref_aliases: true)
|
164
|
+
#
|
165
|
+
# # Output:
|
166
|
+
# # ---
|
167
|
+
# # Burgers:
|
168
|
+
# # Classic:
|
169
|
+
# # Sauce: ['Ketchup', 'Mustard']
|
170
|
+
# # Cheese: 'American'
|
171
|
+
# # Bun: 'Sesame Seed'
|
172
|
+
# # BBQ: {Sauce: 'Honey BBQ', Cheese: 'Cheddar', Bun: 'Kaiser'}
|
173
|
+
# # Fancy:
|
174
|
+
# # Sauce: 'Spicy Wasabi'
|
175
|
+
# # Cheese: 'Smoked Gouda'
|
176
|
+
# # Bun: 'Hawaiian'
|
177
|
+
# # Toppings:
|
178
|
+
# # - 'Mushrooms'
|
179
|
+
# # - ['Spinach', 'Onions', 'Pickles', 'Tomatoes']
|
180
|
+
# # - [['Ketchup', 'Mustard'], ['Salt', 'Pepper']]
|
181
|
+
# # Favorite:
|
182
|
+
# # Sauce: 'Honey BBQ'
|
183
|
+
# # Cheese: 'Cheddar'
|
184
|
+
# # Bun: 'Kaiser'
|
185
|
+
#
|
186
|
+
# @example Class example
|
187
|
+
# require 'psychgus'
|
188
|
+
#
|
189
|
+
# class Burger
|
190
|
+
# attr_accessor :bun
|
191
|
+
# attr_accessor :cheese
|
192
|
+
# attr_accessor :sauce
|
193
|
+
#
|
194
|
+
# def initialize(sauce,cheese,bun)
|
195
|
+
# @bun = bun
|
196
|
+
# @cheese = cheese
|
197
|
+
# @sauce = sauce
|
198
|
+
# end
|
199
|
+
#
|
200
|
+
# # You can still use Psych's encode_with(), no problem
|
201
|
+
# #def encode_with(coder)
|
202
|
+
# # coder['Bun'] = @bun
|
203
|
+
# # coder['Cheese'] = @cheese
|
204
|
+
# # coder['Sauce'] = @sauce
|
205
|
+
# #end
|
206
|
+
# end
|
207
|
+
#
|
208
|
+
# class Burgers
|
209
|
+
# include Psychgus::Blueberry
|
210
|
+
#
|
211
|
+
# attr_accessor :burgers
|
212
|
+
# attr_accessor :toppings
|
213
|
+
# attr_accessor :favorite
|
214
|
+
#
|
215
|
+
# def initialize()
|
216
|
+
# @burgers = {
|
217
|
+
# 'Classic' => Burger.new(['Ketchup','Mustard'],'American','Sesame Seed'),
|
218
|
+
# 'BBQ' => Burger.new('Honey BBQ','Cheddar','Kaiser'),
|
219
|
+
# 'Fancy' => Burger.new('Spicy Wasabi','Smoked Gouda','Hawaiian')
|
220
|
+
# }
|
221
|
+
#
|
222
|
+
# @toppings = [
|
223
|
+
# 'Mushrooms',
|
224
|
+
# %w(Lettuce Onions Pickles Tomatoes),
|
225
|
+
# [%w(Ketchup Mustard),%w(Salt Pepper)]
|
226
|
+
# ]
|
227
|
+
#
|
228
|
+
# @favorite = @burgers['BBQ'] # Alias
|
229
|
+
# end
|
230
|
+
#
|
231
|
+
# def psychgus_stylers(sniffer)
|
232
|
+
# return BurgerStyler.new(sniffer)
|
233
|
+
# end
|
234
|
+
#
|
235
|
+
# # You can still use Psych's encode_with(), no problem
|
236
|
+
# #def encode_with(coder)
|
237
|
+
# # coder['Burgers'] = @burgers
|
238
|
+
# # coder['Toppings'] = @toppings
|
239
|
+
# # coder['Favorite'] = @favorite
|
240
|
+
# #end
|
241
|
+
# end
|
242
|
+
#
|
243
|
+
# burgers = Burgers.new
|
244
|
+
# puts burgers.to_yaml(indent: 3,deref_aliases: true)
|
245
|
+
#
|
246
|
+
# # Output:
|
247
|
+
# # ---
|
248
|
+
# # Burgers:
|
249
|
+
# # Classic:
|
250
|
+
# # Bun: 'Sesame Seed'
|
251
|
+
# # Cheese: 'American'
|
252
|
+
# # Sauce:
|
253
|
+
# # - 'Ketchup'
|
254
|
+
# # - 'Mustard'
|
255
|
+
# # BBQ: {Bun: 'Kaiser', Cheese: 'Cheddar', Sauce: 'Honey BBQ'}
|
256
|
+
# # Fancy:
|
257
|
+
# # Bun: 'Hawaiian'
|
258
|
+
# # Cheese: 'Smoked Gouda'
|
259
|
+
# # Sauce: 'Spicy Wasabi'
|
260
|
+
# # Toppings:
|
261
|
+
# # - 'Mushrooms'
|
262
|
+
# # - ['Spinach', 'Onions', 'Pickles', 'Tomatoes']
|
263
|
+
# # - [['Ketchup', 'Mustard'], ['Salt', 'Pepper']]
|
264
|
+
# # Favorite:
|
265
|
+
# # Bun: 'Kaiser'
|
266
|
+
# # Cheese: 'Cheddar'
|
267
|
+
# # Sauce: 'Honey BBQ'
|
268
|
+
#
|
269
|
+
# @example Emitting / Parsing examples
|
270
|
+
# styler = BurgerStyler.new()
|
271
|
+
# options = {:indentation=>3,:stylers=>styler,:deref_aliases=>true}
|
272
|
+
# yaml = burgers.to_yaml(options)
|
273
|
+
#
|
274
|
+
# # High-level emitting
|
275
|
+
# Psychgus.dump(burgers,options)
|
276
|
+
# Psychgus.dump_file('burgers.yaml',burgers,options)
|
277
|
+
# burgers.to_yaml(options)
|
278
|
+
#
|
279
|
+
# # High-level parsing
|
280
|
+
# # - Because to_ruby() will be called, just use Psych:
|
281
|
+
# # - load(), load_file(), load_stream(), safe_load()
|
282
|
+
#
|
283
|
+
# # Mid-level emitting
|
284
|
+
# stream = Psychgus.parse_stream(yaml,stylers: styler,deref_aliases: true)
|
285
|
+
#
|
286
|
+
# stream.to_yaml()
|
287
|
+
#
|
288
|
+
# # Mid-level parsing
|
289
|
+
# Psychgus.parse(yaml,stylers: styler,deref_aliases: true)
|
290
|
+
# Psychgus.parse_file('burgers.yaml',stylers: styler,deref_aliases: true)
|
291
|
+
# Psychgus.parse_stream(yaml,stylers: styler,deref_aliases: true)
|
292
|
+
#
|
293
|
+
# # Low-level emitting
|
294
|
+
# tree_builder = Psychgus::StyledTreeBuilder.new(styler,deref_aliases: true)
|
295
|
+
# visitor = Psych::Visitors::YAMLTree.create(options,tree_builder)
|
296
|
+
#
|
297
|
+
# visitor << burgers
|
298
|
+
# visitor.tree.to_yaml
|
299
|
+
#
|
300
|
+
# # Low-level parsing
|
301
|
+
# parser = Psychgus.parser(stylers: styler,deref_aliases: true)
|
302
|
+
#
|
303
|
+
# parser.parse(yaml)
|
304
|
+
# parser.handler
|
305
|
+
# parser.handler.root
|
306
|
+
#
|
307
|
+
# @author Jonathan Bradley Whited (@esotericpig)
|
308
|
+
# @since 1.0.0
|
309
|
+
###
|
310
|
+
module Psychgus
|
311
|
+
NODE_CLASS_ALIASES = {:Doc => :Document,:Map => :Mapping,:Seq => :Sequence}
|
312
|
+
OPTIONS_ALIASES = {:canon => :canonical,:indent => :indentation}
|
313
|
+
|
314
|
+
# Get a Class (constant) from Psych::Nodes.
|
315
|
+
#
|
316
|
+
# Some +name+s have aliases:
|
317
|
+
# :doc => :document
|
318
|
+
# :map => :mapping
|
319
|
+
# :seq => :sequence
|
320
|
+
#
|
321
|
+
# @param name [Symbol,String] the name of the class from Psych::Nodes
|
322
|
+
#
|
323
|
+
# @return [Class] a class from Psych::Nodes
|
324
|
+
#
|
325
|
+
# @see Psych::Nodes
|
326
|
+
# @see NODE_CLASS_ALIASES
|
327
|
+
def self.node_class(name)
|
328
|
+
name = name.to_sym().capitalize()
|
329
|
+
|
330
|
+
name_alias = NODE_CLASS_ALIASES[name]
|
331
|
+
name = name_alias unless name_alias.nil?()
|
332
|
+
|
333
|
+
return Psych::Nodes.const_get(name)
|
334
|
+
end
|
335
|
+
|
336
|
+
# Get a constant from a Psych::Nodes class (using {.node_class}).
|
337
|
+
#
|
338
|
+
# @param class_name [Symbol,String] the name of the class to get using {.node_class}
|
339
|
+
# @param const_name [Symbol,String] the constant to get from the class
|
340
|
+
# @param lenient [true,false] if true, will return 0 if not const_defined?(), else raise an error
|
341
|
+
#
|
342
|
+
# @return [Integer,Object] the constant value from the class (usually an int)
|
343
|
+
#
|
344
|
+
# @see .node_class
|
345
|
+
def self.node_const(class_name,const_name,lenient=true)
|
346
|
+
node_class = node_class(class_name)
|
347
|
+
const_name = const_name.to_sym().upcase()
|
348
|
+
|
349
|
+
return 0 if lenient && !node_class.const_defined?(const_name,true)
|
350
|
+
return node_class.const_get(const_name,true)
|
351
|
+
end
|
352
|
+
|
353
|
+
MAPPING_ANY = node_const(:mapping,:any)
|
354
|
+
MAPPING_BLOCK = node_const(:mapping,:block)
|
355
|
+
MAPPING_FLOW = node_const(:mapping,:flow)
|
356
|
+
MAP_ANY = MAPPING_ANY
|
357
|
+
MAP_BLOCK = MAPPING_BLOCK
|
358
|
+
MAP_FLOW = MAPPING_FLOW
|
359
|
+
|
360
|
+
SCALAR_ANY = node_const(:scalar,:any)
|
361
|
+
SCALAR_PLAIN = node_const(:scalar,:plain)
|
362
|
+
SCALAR_SINGLE_QUOTED = node_const(:scalar,:single_quoted)
|
363
|
+
SCALAR_DOUBLE_QUOTED = node_const(:scalar,:double_quoted)
|
364
|
+
SCALAR_LITERAL = node_const(:scalar,:literal)
|
365
|
+
SCALAR_FOLDED = node_const(:scalar,:folded)
|
366
|
+
|
367
|
+
SEQUENCE_ANY = node_const(:sequence,:any)
|
368
|
+
SEQUENCE_BLOCK = node_const(:sequence,:block)
|
369
|
+
SEQUENCE_FLOW = node_const(:sequence,:flow)
|
370
|
+
SEQ_ANY = SEQUENCE_ANY
|
371
|
+
SEQ_BLOCK = SEQUENCE_BLOCK
|
372
|
+
SEQ_FLOW = SEQUENCE_FLOW
|
373
|
+
|
374
|
+
STREAM_ANY = node_const(:stream,:any)
|
375
|
+
STREAM_UTF8 = node_const(:stream,:utf8)
|
376
|
+
STREAM_UTF16LE = node_const(:stream,:utf16le)
|
377
|
+
STREAM_UTF16BE = node_const(:stream,:utf16be)
|
378
|
+
|
379
|
+
# Convert +object+ to YAML and dump to +io+.
|
380
|
+
#
|
381
|
+
# +object+, +io+, and +options+ are used like in Psych.dump so can be a drop-in replacement for Psych.
|
382
|
+
#
|
383
|
+
# @param object [Object] the Object to convert to YAML and dump
|
384
|
+
# @param io [nil,IO,Hash] the IO to dump the YAML to or the +options+ Hash; if nil, will use StringIO
|
385
|
+
# @param options [Hash] the options (or keyword args) to use; see {.dump_stream}
|
386
|
+
#
|
387
|
+
# @return [String,Object] the result of converting +object+ to YAML using the params
|
388
|
+
#
|
389
|
+
# @see .dump_stream
|
390
|
+
# @see Psych.dump_stream
|
391
|
+
def self.dump(object,io=nil,**options)
|
392
|
+
return dump_stream(object,io: io,**options)
|
393
|
+
end
|
394
|
+
|
395
|
+
# Convert +objects+ to YAML and dump to a file.
|
396
|
+
#
|
397
|
+
# @example
|
398
|
+
# Psychgus.dump_file('my_dir/my_file.yaml',my_object1,my_object2,mode: 'w:UTF-16',
|
399
|
+
# stylers: MyStyler.new())
|
400
|
+
# Psychgus.dump_file('my_file.yaml',my_object,stylers: [MyStyler1.new(),MyStyler2.new()])
|
401
|
+
#
|
402
|
+
# @param filename [String] the name of the file (and path) to dump to
|
403
|
+
# @param objects [Object,Array<Object>] the Object(s) to convert to YAML and dump
|
404
|
+
# @param mode [String,Integer] the IO open mode to use; examples:
|
405
|
+
# [+'w:UTF-8'+] create a new file or truncate an existing file
|
406
|
+
# and use UTF-8 encoding;
|
407
|
+
# [+'a:UTF-16'+] create a new file or append to an existing file
|
408
|
+
# and use UTF-16 encoding
|
409
|
+
# @param perm [Integer] the permission bits to use (platform dependent)
|
410
|
+
# @param opt [Symbol] the option(s) to use, more readable alternative to +mode+;
|
411
|
+
# examples: +:textmode+, +:autoclose+
|
412
|
+
# @param options [Hash] the options (or keyword args) to use; see {.dump_stream}
|
413
|
+
#
|
414
|
+
# @see .dump_stream
|
415
|
+
# @see File.open
|
416
|
+
# @see IO.new
|
417
|
+
# @see https://ruby-doc.org/core/IO.html#method-c-new
|
418
|
+
def self.dump_file(filename,*objects,mode: 'w',perm: nil,opt: nil,**options)
|
419
|
+
File.open(filename,mode,perm,opt) do |file|
|
420
|
+
file.write(dump_stream(*objects,**options))
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# Convert +objects+ to YAML and dump to +io+.
|
425
|
+
#
|
426
|
+
# +io+ and +options+ are used like in Psych.dump so can be a drop-in replacement for Psych.
|
427
|
+
#
|
428
|
+
# @param objects [Object,Array<Object>] the Object(s) to convert to YAML and dump
|
429
|
+
# @param io [nil,IO,Hash] the IO to dump the YAML to or the +options+ Hash; if nil, will use StringIO
|
430
|
+
# @param stylers [nil,Styler,Array<Styler>] the Styler(s) to use when converting to YAML
|
431
|
+
# @param deref_aliases [true,false] whether to dereference aliases; output the actual value
|
432
|
+
# instead of the alias
|
433
|
+
# @param options [Hash] the options (or keyword args) to use when converting to YAML:
|
434
|
+
# [+:indent+] Alias for +:indentation+. +:indentation+ will override this.
|
435
|
+
# [+:indentation+] Default: +2+.
|
436
|
+
# Number of space characters used to indent.
|
437
|
+
# Acceptable value should be in +0..9+ range, else ignored.
|
438
|
+
# [+:line_width+] Default: +0+ (meaning "wrap at 81").
|
439
|
+
# Max character to wrap line at.
|
440
|
+
# [+:canon+] Alias for +:canonical+. +:canonical+ will override this.
|
441
|
+
# [+:canonical+] Default: +false+.
|
442
|
+
# Write "canonical" YAML form (very verbose, yet strictly formal).
|
443
|
+
# [+:header+] Default: +false+.
|
444
|
+
# Write +%YAML [version]+ at the beginning of document.
|
445
|
+
#
|
446
|
+
# @return [String,Object] the result of converting +object+ to YAML using the params
|
447
|
+
#
|
448
|
+
# @see Psych.dump_stream
|
449
|
+
# @see OPTIONS_ALIASES
|
450
|
+
def self.dump_stream(*objects,io: nil,stylers: nil,deref_aliases: false,**options)
|
451
|
+
if Hash === io
|
452
|
+
options = io
|
453
|
+
io = nil
|
454
|
+
end
|
455
|
+
|
456
|
+
if !options.nil?()
|
457
|
+
OPTIONS_ALIASES.each do |option_alias,option|
|
458
|
+
if options.key?(option_alias) && !options.key?(option)
|
459
|
+
options[option] = options[option_alias]
|
460
|
+
end
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
visitor = Psych::Visitors::YAMLTree.create(options,StyledTreeBuilder.new(*stylers,
|
465
|
+
deref_aliases: deref_aliases))
|
466
|
+
|
467
|
+
objects.each do |object|
|
468
|
+
visitor << object
|
469
|
+
end
|
470
|
+
|
471
|
+
return visitor.tree.yaml(io,options)
|
472
|
+
end
|
473
|
+
|
474
|
+
# Parse +yaml+ into a Psych::Nodes::Document.
|
475
|
+
#
|
476
|
+
# If you're just going to call to_ruby(), then using this method is unnecessary,
|
477
|
+
# and the styler(s) will do nothing for you.
|
478
|
+
#
|
479
|
+
# @param yaml [String] the YAML to parse
|
480
|
+
# @param kargs [Hash] the keyword args to use; see {.parse_stream}
|
481
|
+
#
|
482
|
+
# @return [Psych::Nodes::Document] the parsed Document node
|
483
|
+
#
|
484
|
+
# @see .parse_stream
|
485
|
+
# @see Psych.parse
|
486
|
+
# @see Psych::Nodes::Document
|
487
|
+
def self.parse(yaml,**kargs)
|
488
|
+
parse_stream(yaml,**kargs) do |node|
|
489
|
+
return node
|
490
|
+
end
|
491
|
+
|
492
|
+
return false
|
493
|
+
end
|
494
|
+
|
495
|
+
# Parse a YAML file into a Psych::Nodes::Document.
|
496
|
+
#
|
497
|
+
# If you're just going to call to_ruby(), then using this method is unnecessary,
|
498
|
+
# and the styler(s) will do nothing for you.
|
499
|
+
#
|
500
|
+
# @param filename [String] the name of the YAML file (and path) to parse
|
501
|
+
# @param fallback [Object] the return value when nothing is parsed
|
502
|
+
# @param mode [String,Integer] the IO open mode to use; example: +'r:BOM|UTF-8'+
|
503
|
+
# @param kargs [Hash] the keyword args to use; see {.parse_stream}
|
504
|
+
#
|
505
|
+
# @return [Psych::Nodes::Document] the parsed Document node
|
506
|
+
#
|
507
|
+
# @see .parse_stream
|
508
|
+
# @see Psych.parse_file
|
509
|
+
# @see Psych::Nodes::Document
|
510
|
+
# @see File.open
|
511
|
+
# @see IO.new
|
512
|
+
def self.parse_file(filename,fallback: false,mode: 'r:BOM|UTF-8',**kargs)
|
513
|
+
result = File.open(filename,mode) do |file|
|
514
|
+
parse(file,filename: filename,**kargs)
|
515
|
+
end
|
516
|
+
|
517
|
+
return result || fallback
|
518
|
+
end
|
519
|
+
|
520
|
+
# Parse +yaml+ into a Psych::Nodes::Stream for one document or for multiple documents in one YAML.
|
521
|
+
#
|
522
|
+
# If you're just going to call to_ruby(), then using this method is unnecessary,
|
523
|
+
# and the styler(s) will do nothing for you.
|
524
|
+
#
|
525
|
+
# @example
|
526
|
+
# burgers = <<EOY
|
527
|
+
# ---
|
528
|
+
# Burgers:
|
529
|
+
# Classic:
|
530
|
+
# BBQ: {Sauce: Honey BBQ, Cheese: Cheddar, Bun: Kaiser}
|
531
|
+
# ---
|
532
|
+
# Toppings:
|
533
|
+
# - [Mushrooms, Mustard]
|
534
|
+
# - [Salt, Pepper, Pickles]
|
535
|
+
# ---
|
536
|
+
# `Invalid`
|
537
|
+
# EOY
|
538
|
+
#
|
539
|
+
# i = 0
|
540
|
+
#
|
541
|
+
# begin
|
542
|
+
# Psychgus.parse_stream(burgers,filename: 'burgers.yaml') do |document|
|
543
|
+
# puts "Document ##{i += 1}"
|
544
|
+
# puts document.to_ruby
|
545
|
+
# end
|
546
|
+
# rescue Psych::SyntaxError => err
|
547
|
+
# puts "File: #{err.file}"
|
548
|
+
# end
|
549
|
+
#
|
550
|
+
# # Output:
|
551
|
+
# # Document #1
|
552
|
+
# # {"Burgers"=>{"Classic"=>{"BBQ"=>{"Sauce"=>"Honey BBQ", "Cheese"=>"Cheddar", "Bun"=>"Kaiser"}}}}
|
553
|
+
# # Document #2
|
554
|
+
# # {"Toppings"=>[["Mushrooms", "Mustard"], ["Salt", "Pepper", "Pickles"]]}
|
555
|
+
# # File: burgers.yaml
|
556
|
+
#
|
557
|
+
# @param yaml [String] the YAML to parse
|
558
|
+
# @param filename [String] the filename to pass as +file+ to the Error potentially raised
|
559
|
+
# @param stylers [nil,Styler,Array<Styler>] the Styler(s) to use when parsing the YAML
|
560
|
+
# @param deref_aliases [true,false] whether to dereference aliases; output the actual value
|
561
|
+
# instead of the alias
|
562
|
+
# @param block [Proc] an optional block for parsing multiple documents
|
563
|
+
#
|
564
|
+
# @return [Psych::Nodes::Stream] the parsed Stream node
|
565
|
+
#
|
566
|
+
# @see StyledDocumentStream
|
567
|
+
# @see Psych.parse_stream
|
568
|
+
# @see Psych::Nodes::Stream
|
569
|
+
# @see Psych::SyntaxError
|
570
|
+
def self.parse_stream(yaml,filename: nil,stylers: nil,deref_aliases: false,**options,&block)
|
571
|
+
if block_given?()
|
572
|
+
parser = Psych::Parser.new(StyledDocumentStream.new(*stylers,deref_aliases: deref_aliases,**options,
|
573
|
+
&block))
|
574
|
+
|
575
|
+
return parser.parse(yaml,filename)
|
576
|
+
else
|
577
|
+
parser = self.parser(stylers: stylers,deref_aliases: deref_aliases,**options)
|
578
|
+
parser.parse(yaml,filename)
|
579
|
+
|
580
|
+
return parser.handler.root
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
# Create a new styled Psych::Parser for parsing YAML.
|
585
|
+
#
|
586
|
+
# @example
|
587
|
+
# class CoffeeStyler
|
588
|
+
# include Psychgus::Styler
|
589
|
+
#
|
590
|
+
# def style_sequence(sniffer,node)
|
591
|
+
# node.style = Psychgus::SEQUENCE_FLOW
|
592
|
+
# end
|
593
|
+
# end
|
594
|
+
#
|
595
|
+
# coffee = <<EOY
|
596
|
+
# Coffee:
|
597
|
+
# Roast:
|
598
|
+
# - Light
|
599
|
+
# - Medium
|
600
|
+
# - Dark
|
601
|
+
# Style:
|
602
|
+
# - Cappuccino
|
603
|
+
# - Latte
|
604
|
+
# - Mocha
|
605
|
+
# EOY
|
606
|
+
#
|
607
|
+
# parser = Psychgus.parser(stylers: CoffeeStyler.new)
|
608
|
+
# parser.parse(coffee)
|
609
|
+
# puts parser.handler.root.to_yaml
|
610
|
+
#
|
611
|
+
# # Output:
|
612
|
+
# # Coffee:
|
613
|
+
# # Roast: [Light, Medium, Dark]
|
614
|
+
# # Style: [Cappuccino, Latte, Mocha]
|
615
|
+
#
|
616
|
+
# @param stylers [nil,Styler,Array<Styler>] the Styler(s) to use when parsing the YAML
|
617
|
+
# @param deref_aliases [true,false] whether to dereference aliases; output the actual value
|
618
|
+
# instead of the alias
|
619
|
+
#
|
620
|
+
# @return [Psych::Parser] the new styled Parser
|
621
|
+
#
|
622
|
+
# @see StyledTreeBuilder
|
623
|
+
# @see Psych.parser
|
624
|
+
def self.parser(stylers: nil,deref_aliases: false,**options)
|
625
|
+
return Psych::Parser.new(StyledTreeBuilder.new(*stylers,deref_aliases: deref_aliases,**options))
|
626
|
+
end
|
627
|
+
|
628
|
+
###
|
629
|
+
# Unnecessary Methods
|
630
|
+
#
|
631
|
+
# All of the below methods are not needed, but are defined
|
632
|
+
# so that Psychgus can be a drop-in replacement for Psych.
|
633
|
+
#
|
634
|
+
# Instead, you should probably use Psych.
|
635
|
+
# This is also the recommended practice in case your version
|
636
|
+
# of Psych defines the method differently.
|
637
|
+
#
|
638
|
+
# Private methods of Psych are not defined.
|
639
|
+
#
|
640
|
+
# Because extend is used, do not prefix methods with "self."
|
641
|
+
#
|
642
|
+
# @author Jonathan Bradley Whited (@esotericpig)
|
643
|
+
# @since 1.0.0
|
644
|
+
###
|
645
|
+
module PsychDropIn
|
646
|
+
# @see Psych.add_builtin_type
|
647
|
+
def add_builtin_type(*args,&block)
|
648
|
+
Psych.add_builtin_type(*args,&block)
|
649
|
+
end
|
650
|
+
|
651
|
+
# @see Psych.add_domain_type
|
652
|
+
def add_domain_type(*args,&block)
|
653
|
+
Psych.add_domain_type(*args,&block)
|
654
|
+
end
|
655
|
+
|
656
|
+
# @see Psych.add_tag
|
657
|
+
def add_tag(*args)
|
658
|
+
Psych.add_tag(*args)
|
659
|
+
end
|
660
|
+
|
661
|
+
# @see Psych.load
|
662
|
+
def load(*args,**kargs)
|
663
|
+
Psych.load(*args,**kargs)
|
664
|
+
end
|
665
|
+
|
666
|
+
# @see Psych.load_file
|
667
|
+
def load_file(*args,**kargs)
|
668
|
+
Psych.load_file(*args,**kargs)
|
669
|
+
end
|
670
|
+
|
671
|
+
# @see Psych.load_stream
|
672
|
+
def load_stream(*args,**kargs)
|
673
|
+
Psych.load_stream(*args,**kargs)
|
674
|
+
end
|
675
|
+
|
676
|
+
# @see Psych.remove_type
|
677
|
+
def remove_type(*args)
|
678
|
+
Psych.remove_type(*args)
|
679
|
+
end
|
680
|
+
|
681
|
+
# @see Psych.safe_load
|
682
|
+
def safe_load(*args,**kargs)
|
683
|
+
Psych.safe_load(*args,**kargs)
|
684
|
+
end
|
685
|
+
|
686
|
+
# @see Psych.to_json
|
687
|
+
def to_json(*args)
|
688
|
+
Psych.to_json(*args)
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
extend PsychDropIn
|
693
|
+
end
|