psychgus 1.2.0 → 1.3.4
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 +4 -4
- data/.yardopts +4 -0
- data/CHANGELOG.md +58 -3
- data/Gemfile +0 -18
- data/README.md +38 -43
- data/Rakefile +33 -126
- data/lib/psychgus.rb +178 -186
- data/lib/psychgus/blueberry.rb +28 -39
- data/lib/psychgus/ext.rb +6 -17
- data/lib/psychgus/ext/core_ext.rb +17 -28
- data/lib/psychgus/ext/node_ext.rb +13 -24
- data/lib/psychgus/ext/yaml_tree_ext.rb +23 -34
- data/lib/psychgus/stylables.rb +78 -89
- data/lib/psychgus/styled_document_stream.rb +14 -25
- data/lib/psychgus/styled_tree_builder.rb +90 -101
- data/lib/psychgus/styler.rb +33 -44
- data/lib/psychgus/stylers.rb +67 -78
- data/lib/psychgus/super_sniffer.rb +128 -138
- data/lib/psychgus/super_sniffer/parent.rb +48 -149
- data/lib/psychgus/version.rb +4 -16
- data/psychgus.gemspec +46 -49
- data/test/blueberry_test.rb +30 -41
- data/test/psychgus_test.rb +73 -54
- data/test/psychgus_tester.rb +23 -31
- data/test/sniffer_test.rb +18 -32
- data/test/styler_test.rb +20 -31
- data/test/stylers_test.rb +32 -43
- metadata +38 -23
data/lib/psychgus.rb
CHANGED
@@ -1,22 +1,11 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
1
|
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
4
|
#--
|
5
5
|
# This file is part of Psychgus.
|
6
|
-
# Copyright (c) 2017-
|
7
|
-
#
|
8
|
-
#
|
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/>.
|
6
|
+
# Copyright (c) 2017-2021 Jonathan Bradley Whited
|
7
|
+
#
|
8
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
20
9
|
#++
|
21
10
|
|
22
11
|
|
@@ -42,22 +31,22 @@ require 'psychgus/super_sniffer/parent'
|
|
42
31
|
# Psychgus uses the core standard library {https://github.com/ruby/psych Psych} for working with YAML
|
43
32
|
# and extends it so that developers can easily style the YAML according to their needs.
|
44
33
|
# Thank you to the people that worked and continue to work hard on that project.
|
45
|
-
#
|
34
|
+
#
|
46
35
|
# The name comes from the well-styled character Gus from the TV show Psych.
|
47
|
-
#
|
36
|
+
#
|
48
37
|
# == Create a Styler
|
49
|
-
#
|
38
|
+
#
|
50
39
|
# First, we will create a {Styler}.
|
51
|
-
#
|
40
|
+
#
|
52
41
|
# All you need to do is add +include Psychgus::Styler+ to a class.
|
53
|
-
#
|
42
|
+
#
|
54
43
|
# Here is a complex {Styler} for the examples below:
|
55
44
|
# require 'psychgus'
|
56
|
-
#
|
45
|
+
#
|
57
46
|
# class BurgerStyler
|
58
47
|
# # Mix in methods needed for styling
|
59
48
|
# include Psychgus::Styler
|
60
|
-
#
|
49
|
+
#
|
61
50
|
# def initialize(sniffer=nil)
|
62
51
|
# if sniffer.nil?()
|
63
52
|
# @class_level = 0
|
@@ -68,48 +57,48 @@ require 'psychgus/super_sniffer/parent'
|
|
68
57
|
# @class_position = sniffer.position
|
69
58
|
# end
|
70
59
|
# end
|
71
|
-
#
|
60
|
+
#
|
72
61
|
# # Style all nodes (Psych::Nodes::Node)
|
73
62
|
# def style(sniffer,node)
|
74
63
|
# # Remove "!ruby/object:..." for classes
|
75
64
|
# node.tag = nil if node.node_of?(:mapping,:scalar,:sequence)
|
76
|
-
#
|
65
|
+
#
|
77
66
|
# # This is another way to do the above
|
78
67
|
# #node.tag = nil if node.respond_to?(:tag=)
|
79
68
|
# end
|
80
|
-
#
|
69
|
+
#
|
81
70
|
# # Style aliases (Psych::Nodes::Alias)
|
82
71
|
# def style_alias(sniffer,node)
|
83
72
|
# end
|
84
|
-
#
|
73
|
+
#
|
85
74
|
# # Style maps (Psych::Nodes::Mapping)
|
86
75
|
# # - Hashes (key/value pairs)
|
87
76
|
# # - Example: "Burgers: Classic {}"
|
88
77
|
# def style_mapping(sniffer,node)
|
89
78
|
# parent = sniffer.parent
|
90
|
-
#
|
79
|
+
#
|
91
80
|
# if !parent.nil?()
|
92
81
|
# # BBQ
|
93
82
|
# node.style = Psychgus::MAPPING_FLOW if parent.node_of?(:scalar) &&
|
94
83
|
# parent.value.casecmp('BBQ') == 0
|
95
84
|
# end
|
96
85
|
# end
|
97
|
-
#
|
86
|
+
#
|
98
87
|
# # Style scalars (Psych::Nodes::Scalar)
|
99
88
|
# # - Any text (non-alias)
|
100
89
|
# def style_scalar(sniffer,node)
|
101
90
|
# parent = sniffer.parent
|
102
|
-
#
|
91
|
+
#
|
103
92
|
# # Single quote scalars that are not keys to a map
|
104
93
|
# # - "child_key?" is the same as "child_type == :key"
|
105
94
|
# node.style = Psychgus::SCALAR_SINGLE_QUOTED unless parent.child_key?()
|
106
|
-
#
|
95
|
+
#
|
107
96
|
# # Remove colon (change symbols into strings)
|
108
97
|
# node.value = node.value.sub(':','')
|
109
|
-
#
|
98
|
+
#
|
110
99
|
# # Change lettuce to spinach
|
111
100
|
# node.value = 'Spinach' if node.value.casecmp('Lettuce') == 0
|
112
|
-
#
|
101
|
+
#
|
113
102
|
# # Capitalize each word
|
114
103
|
# node.value = node.value.split(' ').map do |v|
|
115
104
|
# if v.casecmp('BBQ') == 0
|
@@ -119,23 +108,23 @@ require 'psychgus/super_sniffer/parent'
|
|
119
108
|
# end
|
120
109
|
# end.join(' ')
|
121
110
|
# end
|
122
|
-
#
|
111
|
+
#
|
123
112
|
# # Style sequences (Psych::Nodes::Sequence)
|
124
113
|
# # - Arrays
|
125
114
|
# # - Example: "[Lettuce, Onions, Pickles, Tomatoes]"
|
126
115
|
# def style_sequence(sniffer,node)
|
127
116
|
# relative_level = (sniffer.level - @class_level) + 1
|
128
|
-
#
|
117
|
+
#
|
129
118
|
# node.style = Psychgus::SEQUENCE_FLOW if sniffer.level >= 4
|
130
|
-
#
|
119
|
+
#
|
131
120
|
# # Make "[Ketchup, Mustard]" a block for the Class Example
|
132
121
|
# node.style = Psychgus::SEQUENCE_BLOCK if relative_level == 7
|
133
122
|
# end
|
134
123
|
# end
|
135
|
-
#
|
124
|
+
#
|
136
125
|
# @example Hash example
|
137
126
|
# require 'psychgus'
|
138
|
-
#
|
127
|
+
#
|
139
128
|
# burgers = {
|
140
129
|
# :Burgers => {
|
141
130
|
# :Classic => {
|
@@ -161,9 +150,9 @@ require 'psychgus/super_sniffer/parent'
|
|
161
150
|
# ]
|
162
151
|
# }
|
163
152
|
# burgers[:Favorite] = burgers[:Burgers][:BBQ] # Alias
|
164
|
-
#
|
153
|
+
#
|
165
154
|
# puts burgers.to_yaml(indent: 3,stylers: BurgerStyler.new,deref_aliases: true)
|
166
|
-
#
|
155
|
+
#
|
167
156
|
# # Output:
|
168
157
|
# # ---
|
169
158
|
# # Burgers:
|
@@ -184,21 +173,21 @@ require 'psychgus/super_sniffer/parent'
|
|
184
173
|
# # Sauce: 'Honey BBQ'
|
185
174
|
# # Cheese: 'Cheddar'
|
186
175
|
# # Bun: 'Kaiser'
|
187
|
-
#
|
176
|
+
#
|
188
177
|
# @example Class example
|
189
178
|
# require 'psychgus'
|
190
|
-
#
|
179
|
+
#
|
191
180
|
# class Burger
|
192
181
|
# attr_accessor :bun
|
193
182
|
# attr_accessor :cheese
|
194
183
|
# attr_accessor :sauce
|
195
|
-
#
|
184
|
+
#
|
196
185
|
# def initialize(sauce,cheese,bun)
|
197
186
|
# @bun = bun
|
198
187
|
# @cheese = cheese
|
199
188
|
# @sauce = sauce
|
200
189
|
# end
|
201
|
-
#
|
190
|
+
#
|
202
191
|
# # You can still use Psych's encode_with(), no problem
|
203
192
|
# #def encode_with(coder)
|
204
193
|
# # coder['Bun'] = @bun
|
@@ -206,34 +195,34 @@ require 'psychgus/super_sniffer/parent'
|
|
206
195
|
# # coder['Sauce'] = @sauce
|
207
196
|
# #end
|
208
197
|
# end
|
209
|
-
#
|
198
|
+
#
|
210
199
|
# class Burgers
|
211
200
|
# include Psychgus::Blueberry
|
212
|
-
#
|
201
|
+
#
|
213
202
|
# attr_accessor :burgers
|
214
203
|
# attr_accessor :toppings
|
215
204
|
# attr_accessor :favorite
|
216
|
-
#
|
205
|
+
#
|
217
206
|
# def initialize()
|
218
207
|
# @burgers = {
|
219
208
|
# 'Classic' => Burger.new(['Ketchup','Mustard'],'American','Sesame Seed'),
|
220
209
|
# 'BBQ' => Burger.new('Honey BBQ','Cheddar','Kaiser'),
|
221
210
|
# 'Fancy' => Burger.new('Spicy Wasabi','Smoked Gouda','Hawaiian')
|
222
211
|
# }
|
223
|
-
#
|
212
|
+
#
|
224
213
|
# @toppings = [
|
225
214
|
# 'Mushrooms',
|
226
215
|
# %w(Lettuce Onions Pickles Tomatoes),
|
227
216
|
# [%w(Ketchup Mustard),%w(Salt Pepper)]
|
228
217
|
# ]
|
229
|
-
#
|
218
|
+
#
|
230
219
|
# @favorite = @burgers['BBQ'] # Alias
|
231
220
|
# end
|
232
|
-
#
|
221
|
+
#
|
233
222
|
# def psychgus_stylers(sniffer)
|
234
223
|
# return BurgerStyler.new(sniffer)
|
235
224
|
# end
|
236
|
-
#
|
225
|
+
#
|
237
226
|
# # You can still use Psych's encode_with(), no problem
|
238
227
|
# #def encode_with(coder)
|
239
228
|
# # coder['Burgers'] = @burgers
|
@@ -241,10 +230,10 @@ require 'psychgus/super_sniffer/parent'
|
|
241
230
|
# # coder['Favorite'] = @favorite
|
242
231
|
# #end
|
243
232
|
# end
|
244
|
-
#
|
233
|
+
#
|
245
234
|
# burgers = Burgers.new
|
246
235
|
# puts burgers.to_yaml(indent: 3,deref_aliases: true)
|
247
|
-
#
|
236
|
+
#
|
248
237
|
# # Output:
|
249
238
|
# # ---
|
250
239
|
# # Burgers:
|
@@ -267,146 +256,144 @@ require 'psychgus/super_sniffer/parent'
|
|
267
256
|
# # Bun: 'Kaiser'
|
268
257
|
# # Cheese: 'Cheddar'
|
269
258
|
# # Sauce: 'Honey BBQ'
|
270
|
-
#
|
259
|
+
#
|
271
260
|
# @example Emitting / Parsing examples
|
272
261
|
# styler = BurgerStyler.new()
|
273
262
|
# options = {:indentation=>3,:stylers=>styler,:deref_aliases=>true}
|
274
263
|
# yaml = burgers.to_yaml(options)
|
275
|
-
#
|
264
|
+
#
|
276
265
|
# # High-level emitting
|
277
266
|
# Psychgus.dump(burgers,options)
|
278
267
|
# Psychgus.dump_file('burgers.yaml',burgers,options)
|
279
268
|
# burgers.to_yaml(options)
|
280
|
-
#
|
269
|
+
#
|
281
270
|
# # High-level parsing
|
282
271
|
# # - Because to_ruby() will be called, just use Psych:
|
283
272
|
# # - load(), load_file(), load_stream(), safe_load()
|
284
|
-
#
|
273
|
+
#
|
285
274
|
# # Mid-level emitting
|
286
275
|
# stream = Psychgus.parse_stream(yaml,stylers: styler,deref_aliases: true)
|
287
|
-
#
|
276
|
+
#
|
288
277
|
# stream.to_yaml()
|
289
|
-
#
|
278
|
+
#
|
290
279
|
# # Mid-level parsing
|
291
280
|
# Psychgus.parse(yaml,stylers: styler,deref_aliases: true)
|
292
281
|
# Psychgus.parse_file('burgers.yaml',stylers: styler,deref_aliases: true)
|
293
282
|
# Psychgus.parse_stream(yaml,stylers: styler,deref_aliases: true)
|
294
|
-
#
|
283
|
+
#
|
295
284
|
# # Low-level emitting
|
296
285
|
# tree_builder = Psychgus::StyledTreeBuilder.new(styler,deref_aliases: true)
|
297
286
|
# visitor = Psych::Visitors::YAMLTree.create(options,tree_builder)
|
298
|
-
#
|
287
|
+
#
|
299
288
|
# visitor << burgers
|
300
289
|
# visitor.tree.to_yaml
|
301
|
-
#
|
290
|
+
#
|
302
291
|
# # Low-level parsing
|
303
292
|
# parser = Psychgus.parser(stylers: styler,deref_aliases: true)
|
304
|
-
#
|
293
|
+
#
|
305
294
|
# parser.parse(yaml)
|
306
295
|
# parser.handler
|
307
296
|
# parser.handler.root
|
308
|
-
#
|
309
|
-
# @author Jonathan Bradley Whited
|
297
|
+
#
|
298
|
+
# @author Jonathan Bradley Whited
|
310
299
|
# @since 1.0.0
|
311
300
|
###
|
312
301
|
module Psychgus
|
313
302
|
# Include these in the top namespace for convenience (i.e., less typing).
|
314
|
-
#
|
315
|
-
# @since 1.2.0
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
OPTIONS_ALIASES = {:canon => :canonical,:indent => :indentation}
|
321
|
-
|
303
|
+
include Stylables # @since 1.2.0
|
304
|
+
include Stylers # @since 1.2.0
|
305
|
+
|
306
|
+
NODE_CLASS_ALIASES = {Doc: :Document,Map: :Mapping,Seq: :Sequence}.freeze
|
307
|
+
OPTIONS_ALIASES = {canon: :canonical,indent: :indentation}.freeze
|
308
|
+
|
322
309
|
# Get a Class (constant) from Psych::Nodes.
|
323
|
-
#
|
310
|
+
#
|
324
311
|
# Some +name+s have aliases:
|
325
312
|
# :doc => :document
|
326
313
|
# :map => :mapping
|
327
314
|
# :seq => :sequence
|
328
|
-
#
|
315
|
+
#
|
329
316
|
# @param name [Symbol,String] the name of the class from Psych::Nodes
|
330
|
-
#
|
317
|
+
#
|
331
318
|
# @return [Class] a class from Psych::Nodes
|
332
|
-
#
|
319
|
+
#
|
333
320
|
# @see Psych::Nodes
|
334
321
|
# @see NODE_CLASS_ALIASES
|
335
322
|
def self.node_class(name)
|
336
|
-
name = name.to_sym
|
337
|
-
|
323
|
+
name = name.to_sym.capitalize
|
324
|
+
|
338
325
|
actual_name = NODE_CLASS_ALIASES[name]
|
339
|
-
name = actual_name unless actual_name.nil?
|
340
|
-
|
326
|
+
name = actual_name unless actual_name.nil?
|
327
|
+
|
341
328
|
return Psych::Nodes.const_get(name)
|
342
329
|
end
|
343
|
-
|
330
|
+
|
344
331
|
# Get a constant from a Psych::Nodes class (using {.node_class}).
|
345
|
-
#
|
332
|
+
#
|
346
333
|
# @param class_name [Symbol,String] the name of the class to get using {.node_class}
|
347
334
|
# @param const_name [Symbol,String] the constant to get from the class
|
348
335
|
# @param lenient [true,false] if true, will return 0 if not const_defined?(), else raise an error
|
349
|
-
#
|
336
|
+
#
|
350
337
|
# @return [Integer,Object] the constant value from the class (usually an int)
|
351
|
-
#
|
338
|
+
#
|
352
339
|
# @see .node_class
|
353
340
|
def self.node_const(class_name,const_name,lenient=true)
|
354
341
|
node_class = node_class(class_name)
|
355
|
-
const_name = const_name.to_sym
|
356
|
-
|
342
|
+
const_name = const_name.to_sym.upcase
|
343
|
+
|
357
344
|
return 0 if lenient && !node_class.const_defined?(const_name,true)
|
358
345
|
return node_class.const_get(const_name,true)
|
359
346
|
end
|
360
|
-
|
347
|
+
|
361
348
|
MAPPING_ANY = node_const(:mapping,:any)
|
362
349
|
MAPPING_BLOCK = node_const(:mapping,:block)
|
363
350
|
MAPPING_FLOW = node_const(:mapping,:flow)
|
364
351
|
MAP_ANY = MAPPING_ANY
|
365
352
|
MAP_BLOCK = MAPPING_BLOCK
|
366
353
|
MAP_FLOW = MAPPING_FLOW
|
367
|
-
|
354
|
+
|
368
355
|
SCALAR_ANY = node_const(:scalar,:any)
|
369
356
|
SCALAR_PLAIN = node_const(:scalar,:plain)
|
370
357
|
SCALAR_SINGLE_QUOTED = node_const(:scalar,:single_quoted)
|
371
358
|
SCALAR_DOUBLE_QUOTED = node_const(:scalar,:double_quoted)
|
372
359
|
SCALAR_LITERAL = node_const(:scalar,:literal)
|
373
360
|
SCALAR_FOLDED = node_const(:scalar,:folded)
|
374
|
-
|
361
|
+
|
375
362
|
SEQUENCE_ANY = node_const(:sequence,:any)
|
376
363
|
SEQUENCE_BLOCK = node_const(:sequence,:block)
|
377
364
|
SEQUENCE_FLOW = node_const(:sequence,:flow)
|
378
365
|
SEQ_ANY = SEQUENCE_ANY
|
379
366
|
SEQ_BLOCK = SEQUENCE_BLOCK
|
380
367
|
SEQ_FLOW = SEQUENCE_FLOW
|
381
|
-
|
368
|
+
|
382
369
|
STREAM_ANY = node_const(:stream,:any)
|
383
370
|
STREAM_UTF8 = node_const(:stream,:utf8)
|
384
371
|
STREAM_UTF16LE = node_const(:stream,:utf16le)
|
385
372
|
STREAM_UTF16BE = node_const(:stream,:utf16be)
|
386
|
-
|
373
|
+
|
387
374
|
# Convert +object+ to YAML and dump to +io+.
|
388
|
-
#
|
375
|
+
#
|
389
376
|
# +object+, +io+, and +options+ are used like in Psych.dump so can be a drop-in replacement for Psych.
|
390
|
-
#
|
377
|
+
#
|
391
378
|
# @param object [Object] the Object to convert to YAML and dump
|
392
379
|
# @param io [nil,IO,Hash] the IO to dump the YAML to or the +options+ Hash; if nil, will use StringIO
|
393
380
|
# @param options [Hash] the options (or keyword args) to use; see {.dump_stream}
|
394
|
-
#
|
381
|
+
#
|
395
382
|
# @return [String,Object] the result of converting +object+ to YAML using the params
|
396
|
-
#
|
383
|
+
#
|
397
384
|
# @see .dump_stream
|
398
385
|
# @see Psych.dump_stream
|
399
386
|
def self.dump(object,io=nil,**options)
|
400
387
|
return dump_stream(object,io: io,**options)
|
401
388
|
end
|
402
|
-
|
389
|
+
|
403
390
|
# Convert +objects+ to YAML and dump to a file.
|
404
|
-
#
|
391
|
+
#
|
405
392
|
# @example
|
406
393
|
# Psychgus.dump_file('my_dir/my_file.yaml',my_object1,my_object2,mode: 'w:UTF-16',
|
407
394
|
# stylers: MyStyler.new())
|
408
395
|
# Psychgus.dump_file('my_file.yaml',my_object,stylers: [MyStyler1.new(),MyStyler2.new()])
|
409
|
-
#
|
396
|
+
#
|
410
397
|
# @param filename [String] the name of the file (and path) to dump to
|
411
398
|
# @param objects [Object,Array<Object>] the Object(s) to convert to YAML and dump
|
412
399
|
# @param mode [String,Integer] the IO open mode to use; examples:
|
@@ -415,24 +402,25 @@ module Psychgus
|
|
415
402
|
# [+'a:UTF-16'+] create a new file or append to an existing file
|
416
403
|
# and use UTF-16 encoding
|
417
404
|
# @param perm [Integer] the permission bits to use (platform dependent)
|
418
|
-
# @param opt [
|
419
|
-
# examples: +:textmode+, +:autoclose+
|
405
|
+
# @param opt [Hash] Hash of keyword args to pass to +File.open()+
|
420
406
|
# @param options [Hash] the options (or keyword args) to use; see {.dump_stream}
|
421
|
-
#
|
407
|
+
#
|
422
408
|
# @see .dump_stream
|
423
409
|
# @see File.open
|
424
410
|
# @see IO.new
|
425
411
|
# @see https://ruby-doc.org/core/IO.html#method-c-new
|
426
412
|
def self.dump_file(filename,*objects,mode: 'w',perm: nil,opt: nil,**options)
|
427
|
-
|
413
|
+
opt = Hash(opt)
|
414
|
+
|
415
|
+
File.open(filename,mode,perm,**opt) do |file|
|
428
416
|
file.write(dump_stream(*objects,**options))
|
429
417
|
end
|
430
418
|
end
|
431
|
-
|
419
|
+
|
432
420
|
# Convert +objects+ to YAML and dump to +io+.
|
433
|
-
#
|
421
|
+
#
|
434
422
|
# +io+ and +options+ are used like in Psych.dump so can be a drop-in replacement for Psych.
|
435
|
-
#
|
423
|
+
#
|
436
424
|
# @param objects [Object,Array<Object>] the Object(s) to convert to YAML and dump
|
437
425
|
# @param io [nil,IO,Hash] the IO to dump the YAML to or the +options+ Hash; if nil, will use StringIO
|
438
426
|
# @param stylers [nil,Styler,Array<Styler>] the Styler(s) to use when converting to YAML
|
@@ -450,47 +438,48 @@ module Psychgus
|
|
450
438
|
# Write "canonical" YAML form (very verbose, yet strictly formal).
|
451
439
|
# [+:header+] Default: +false+.
|
452
440
|
# Write +%YAML [version]+ at the beginning of document.
|
453
|
-
#
|
441
|
+
#
|
454
442
|
# @return [String,Object] the result of converting +object+ to YAML using the params
|
455
|
-
#
|
443
|
+
#
|
456
444
|
# @see Psych.dump_stream
|
457
445
|
# @see OPTIONS_ALIASES
|
458
446
|
def self.dump_stream(*objects,io: nil,stylers: nil,deref_aliases: false,**options)
|
459
447
|
# If you call this method with only a Hash that uses symbols as keys,
|
460
448
|
# then options will be set to the Hash, instead of objects.
|
461
|
-
#
|
449
|
+
#
|
462
450
|
# For example, the below will be stored in options, not objects:
|
463
451
|
# - dump_stream({:coffee => {:roast => [],:style => []}})
|
464
|
-
#
|
452
|
+
#
|
465
453
|
# This if-statement is guaranteed because dump_stream([]) and dump_stream(nil)
|
466
454
|
# will produce [[]] and [nil], which are not empty.
|
467
|
-
#
|
455
|
+
#
|
468
456
|
# dump_stream() w/o any args is the only problem, but resolved w/ [nil].
|
469
|
-
if objects.empty?
|
470
|
-
objects = options.empty?
|
457
|
+
if objects.empty?
|
458
|
+
objects = options.empty? ? [nil] : [options]
|
471
459
|
options = {}
|
472
460
|
end
|
473
|
-
|
474
|
-
if Hash
|
461
|
+
|
462
|
+
if io.is_a?(Hash)
|
475
463
|
options = io
|
476
464
|
io = nil
|
477
465
|
end
|
478
|
-
|
479
|
-
if !options.empty?
|
466
|
+
|
467
|
+
if !options.empty?
|
480
468
|
OPTIONS_ALIASES.each do |option_alias,actual_option|
|
481
469
|
if options.key?(option_alias) && !options.key?(actual_option)
|
482
470
|
options[actual_option] = options[option_alias]
|
483
471
|
end
|
484
472
|
end
|
485
473
|
end
|
486
|
-
|
474
|
+
|
487
475
|
visitor = Psych::Visitors::YAMLTree.create(options,StyledTreeBuilder.new(*stylers,
|
488
476
|
deref_aliases: deref_aliases))
|
489
|
-
|
490
|
-
if objects.empty?
|
477
|
+
|
478
|
+
if objects.empty?
|
491
479
|
# Else, will throw a cryptic NoMethodError:
|
492
|
-
# - "psych/tree_builder.rb:in `set_end_location':
|
493
|
-
#
|
480
|
+
# - "psych/tree_builder.rb:in `set_end_location':
|
481
|
+
# undefined method `end_line=' for nil:NilClass (NoMethodError)"
|
482
|
+
#
|
494
483
|
# This should never occur because of the if-statement at the top of this method.
|
495
484
|
visitor << nil
|
496
485
|
else
|
@@ -498,17 +487,17 @@ module Psychgus
|
|
498
487
|
visitor << object
|
499
488
|
end
|
500
489
|
end
|
501
|
-
|
490
|
+
|
502
491
|
return visitor.tree.yaml(io,options)
|
503
492
|
end
|
504
|
-
|
493
|
+
|
505
494
|
# Get a visual hierarchy of the levels as a String.
|
506
|
-
#
|
495
|
+
#
|
507
496
|
# This is useful for determining the correct level/position when writing a {Styler}.
|
508
|
-
#
|
497
|
+
#
|
509
498
|
# @example
|
510
499
|
# require 'psychgus'
|
511
|
-
#
|
500
|
+
#
|
512
501
|
# burgers = {
|
513
502
|
# :burgers => {
|
514
503
|
# :classic => {:sauce => %w(Ketchup Mustard),
|
@@ -527,9 +516,9 @@ module Psychgus
|
|
527
516
|
# [%w(Ketchup Mustard), %w(Salt Pepper)]
|
528
517
|
# ]
|
529
518
|
# }
|
530
|
-
#
|
519
|
+
#
|
531
520
|
# puts Psychgus.hierarchy(burgers)
|
532
|
-
#
|
521
|
+
#
|
533
522
|
# # Output:
|
534
523
|
# # ---
|
535
524
|
# # (level:position):current_node - <parent:(parent_level:parent_position)>
|
@@ -580,34 +569,34 @@ module Psychgus
|
|
580
569
|
# # (5:2):Psych::Nodes::Sequence - <seq:(4:3)>
|
581
570
|
# # (6:1):Salt - <seq:(5:2)>
|
582
571
|
# # (6:2):Pepper - <seq:(5:2)>
|
583
|
-
#
|
572
|
+
#
|
584
573
|
# @param objects [Object,Array<Object>] the Object(s) to get a visual hierarchy of
|
585
574
|
# @param kargs [Hash] the keyword args to pass to {Stylers::HierarchyStyler} and to {dump_stream}
|
586
|
-
#
|
575
|
+
#
|
587
576
|
# @return [String] the visual hierarchy of levels
|
588
|
-
#
|
577
|
+
#
|
589
578
|
# @see Stylers::HierarchyStyler
|
590
579
|
# @see dump_stream
|
591
|
-
#
|
580
|
+
#
|
592
581
|
# @since 1.2.0
|
593
582
|
def self.hierarchy(*objects,**kargs)
|
594
583
|
styler = Stylers::HierarchyStyler.new(**kargs)
|
595
|
-
|
584
|
+
|
596
585
|
dump_stream(*objects,stylers: styler,**kargs)
|
597
|
-
|
598
|
-
return styler.to_s
|
586
|
+
|
587
|
+
return styler.to_s
|
599
588
|
end
|
600
|
-
|
589
|
+
|
601
590
|
# Parse +yaml+ into a Psych::Nodes::Document.
|
602
|
-
#
|
591
|
+
#
|
603
592
|
# If you're just going to call to_ruby(), then using this method is unnecessary,
|
604
593
|
# and the styler(s) will do nothing for you.
|
605
|
-
#
|
594
|
+
#
|
606
595
|
# @param yaml [String] the YAML to parse
|
607
596
|
# @param kargs [Hash] the keyword args to use; see {.parse_stream}
|
608
|
-
#
|
597
|
+
#
|
609
598
|
# @return [Psych::Nodes::Document] the parsed Document node
|
610
|
-
#
|
599
|
+
#
|
611
600
|
# @see .parse_stream
|
612
601
|
# @see Psych.parse
|
613
602
|
# @see Psych::Nodes::Document
|
@@ -615,40 +604,43 @@ module Psychgus
|
|
615
604
|
parse_stream(yaml,**kargs) do |node|
|
616
605
|
return node
|
617
606
|
end
|
618
|
-
|
607
|
+
|
619
608
|
return false
|
620
609
|
end
|
621
|
-
|
610
|
+
|
622
611
|
# Parse a YAML file into a Psych::Nodes::Document.
|
623
|
-
#
|
612
|
+
#
|
624
613
|
# If you're just going to call to_ruby(), then using this method is unnecessary,
|
625
614
|
# and the styler(s) will do nothing for you.
|
626
|
-
#
|
615
|
+
#
|
627
616
|
# @param filename [String] the name of the YAML file (and path) to parse
|
628
617
|
# @param fallback [Object] the return value when nothing is parsed
|
629
618
|
# @param mode [String,Integer] the IO open mode to use; example: +'r:BOM|UTF-8'+
|
619
|
+
# @param opt [Hash] Hash of keyword args to pass to +File.open()+
|
630
620
|
# @param kargs [Hash] the keyword args to use; see {.parse_stream}
|
631
|
-
#
|
621
|
+
#
|
632
622
|
# @return [Psych::Nodes::Document] the parsed Document node
|
633
|
-
#
|
623
|
+
#
|
634
624
|
# @see .parse_stream
|
635
625
|
# @see Psych.parse_file
|
636
626
|
# @see Psych::Nodes::Document
|
637
627
|
# @see File.open
|
638
628
|
# @see IO.new
|
639
|
-
def self.parse_file(filename,fallback: false,mode: 'r:BOM|UTF-8',**kargs)
|
640
|
-
|
629
|
+
def self.parse_file(filename,fallback: false,mode: 'r:BOM|UTF-8',opt: nil,**kargs)
|
630
|
+
opt = Hash(opt)
|
631
|
+
|
632
|
+
result = File.open(filename,mode,**opt) do |file|
|
641
633
|
parse(file,filename: filename,**kargs)
|
642
634
|
end
|
643
|
-
|
635
|
+
|
644
636
|
return result || fallback
|
645
637
|
end
|
646
|
-
|
638
|
+
|
647
639
|
# Parse +yaml+ into a Psych::Nodes::Stream for one document or for multiple documents in one YAML.
|
648
|
-
#
|
640
|
+
#
|
649
641
|
# If you're just going to call to_ruby(), then using this method is unnecessary,
|
650
642
|
# and the styler(s) will do nothing for you.
|
651
|
-
#
|
643
|
+
#
|
652
644
|
# @example
|
653
645
|
# burgers = <<EOY
|
654
646
|
# ---
|
@@ -662,9 +654,9 @@ module Psychgus
|
|
662
654
|
# ---
|
663
655
|
# `Invalid`
|
664
656
|
# EOY
|
665
|
-
#
|
657
|
+
#
|
666
658
|
# i = 0
|
667
|
-
#
|
659
|
+
#
|
668
660
|
# begin
|
669
661
|
# Psychgus.parse_stream(burgers,filename: 'burgers.yaml') do |document|
|
670
662
|
# puts "Document ##{i += 1}"
|
@@ -673,52 +665,52 @@ module Psychgus
|
|
673
665
|
# rescue Psych::SyntaxError => err
|
674
666
|
# puts "File: #{err.file}"
|
675
667
|
# end
|
676
|
-
#
|
668
|
+
#
|
677
669
|
# # Output:
|
678
670
|
# # Document #1
|
679
671
|
# # {"Burgers"=>{"Classic"=>{"BBQ"=>{"Sauce"=>"Honey BBQ", "Cheese"=>"Cheddar", "Bun"=>"Kaiser"}}}}
|
680
672
|
# # Document #2
|
681
673
|
# # {"Toppings"=>[["Mushrooms", "Mustard"], ["Salt", "Pepper", "Pickles"]]}
|
682
674
|
# # File: burgers.yaml
|
683
|
-
#
|
675
|
+
#
|
684
676
|
# @param yaml [String] the YAML to parse
|
685
677
|
# @param filename [String] the filename to pass as +file+ to the Error potentially raised
|
686
678
|
# @param stylers [nil,Styler,Array<Styler>] the Styler(s) to use when parsing the YAML
|
687
679
|
# @param deref_aliases [true,false] whether to dereference aliases; output the actual value
|
688
680
|
# instead of the alias
|
689
681
|
# @param block [Proc] an optional block for parsing multiple documents
|
690
|
-
#
|
682
|
+
#
|
691
683
|
# @return [Psych::Nodes::Stream] the parsed Stream node
|
692
|
-
#
|
684
|
+
#
|
693
685
|
# @see StyledDocumentStream
|
694
686
|
# @see Psych.parse_stream
|
695
687
|
# @see Psych::Nodes::Stream
|
696
688
|
# @see Psych::SyntaxError
|
697
689
|
def self.parse_stream(yaml,filename: nil,stylers: nil,deref_aliases: false,**options,&block)
|
698
|
-
if block_given?
|
690
|
+
if block_given?
|
699
691
|
parser = Psych::Parser.new(StyledDocumentStream.new(*stylers,deref_aliases: deref_aliases,**options,
|
700
692
|
&block))
|
701
|
-
|
693
|
+
|
702
694
|
return parser.parse(yaml,filename)
|
703
695
|
else
|
704
696
|
parser = self.parser(stylers: stylers,deref_aliases: deref_aliases,**options)
|
705
697
|
parser.parse(yaml,filename)
|
706
|
-
|
698
|
+
|
707
699
|
return parser.handler.root
|
708
700
|
end
|
709
701
|
end
|
710
|
-
|
702
|
+
|
711
703
|
# Create a new styled Psych::Parser for parsing YAML.
|
712
|
-
#
|
704
|
+
#
|
713
705
|
# @example
|
714
706
|
# class CoffeeStyler
|
715
707
|
# include Psychgus::Styler
|
716
|
-
#
|
708
|
+
#
|
717
709
|
# def style_sequence(sniffer,node)
|
718
710
|
# node.style = Psychgus::SEQUENCE_FLOW
|
719
711
|
# end
|
720
712
|
# end
|
721
|
-
#
|
713
|
+
#
|
722
714
|
# coffee = <<EOY
|
723
715
|
# Coffee:
|
724
716
|
# Roast:
|
@@ -730,43 +722,43 @@ module Psychgus
|
|
730
722
|
# - Latte
|
731
723
|
# - Mocha
|
732
724
|
# EOY
|
733
|
-
#
|
725
|
+
#
|
734
726
|
# parser = Psychgus.parser(stylers: CoffeeStyler.new)
|
735
727
|
# parser.parse(coffee)
|
736
728
|
# puts parser.handler.root.to_yaml
|
737
|
-
#
|
729
|
+
#
|
738
730
|
# # Output:
|
739
731
|
# # Coffee:
|
740
732
|
# # Roast: [Light, Medium, Dark]
|
741
733
|
# # Style: [Cappuccino, Latte, Mocha]
|
742
|
-
#
|
734
|
+
#
|
743
735
|
# @param stylers [nil,Styler,Array<Styler>] the Styler(s) to use when parsing the YAML
|
744
736
|
# @param deref_aliases [true,false] whether to dereference aliases; output the actual value
|
745
737
|
# instead of the alias
|
746
|
-
#
|
738
|
+
#
|
747
739
|
# @return [Psych::Parser] the new styled Parser
|
748
|
-
#
|
740
|
+
#
|
749
741
|
# @see StyledTreeBuilder
|
750
742
|
# @see Psych.parser
|
751
743
|
def self.parser(stylers: nil,deref_aliases: false,**options)
|
752
744
|
return Psych::Parser.new(StyledTreeBuilder.new(*stylers,deref_aliases: deref_aliases,**options))
|
753
745
|
end
|
754
|
-
|
746
|
+
|
755
747
|
###
|
756
748
|
# Unnecessary Methods
|
757
|
-
#
|
749
|
+
#
|
758
750
|
# All of the below methods are not needed, but are defined
|
759
751
|
# so that Psychgus can be a drop-in replacement for Psych.
|
760
|
-
#
|
752
|
+
#
|
761
753
|
# Instead, you should probably use Psych.
|
762
754
|
# This is also the recommended practice in case your version
|
763
755
|
# of Psych defines the method differently.
|
764
|
-
#
|
756
|
+
#
|
765
757
|
# Private methods of Psych are not defined.
|
766
|
-
#
|
758
|
+
#
|
767
759
|
# @note For devs/hacking: because extend is used, do not prefix methods with "self."
|
768
|
-
#
|
769
|
-
# @author Jonathan Bradley Whited
|
760
|
+
#
|
761
|
+
# @author Jonathan Bradley Whited
|
770
762
|
# @since 1.0.0
|
771
763
|
###
|
772
764
|
module PsychDropIn
|
@@ -774,47 +766,47 @@ module Psychgus
|
|
774
766
|
def add_builtin_type(*args,&block)
|
775
767
|
Psych.add_builtin_type(*args,&block)
|
776
768
|
end
|
777
|
-
|
769
|
+
|
778
770
|
# @see Psych.add_domain_type
|
779
771
|
def add_domain_type(*args,&block)
|
780
772
|
Psych.add_domain_type(*args,&block)
|
781
773
|
end
|
782
|
-
|
774
|
+
|
783
775
|
# @see Psych.add_tag
|
784
776
|
def add_tag(*args)
|
785
777
|
Psych.add_tag(*args)
|
786
778
|
end
|
787
|
-
|
779
|
+
|
788
780
|
# @see Psych.load
|
789
781
|
def load(*args,**kargs)
|
790
782
|
Psych.load(*args,**kargs)
|
791
783
|
end
|
792
|
-
|
784
|
+
|
793
785
|
# @see Psych.load_file
|
794
786
|
def load_file(*args,**kargs)
|
795
787
|
Psych.load_file(*args,**kargs)
|
796
788
|
end
|
797
|
-
|
789
|
+
|
798
790
|
# @see Psych.load_stream
|
799
791
|
def load_stream(*args,**kargs)
|
800
792
|
Psych.load_stream(*args,**kargs)
|
801
793
|
end
|
802
|
-
|
794
|
+
|
803
795
|
# @see Psych.remove_type
|
804
796
|
def remove_type(*args)
|
805
797
|
Psych.remove_type(*args)
|
806
798
|
end
|
807
|
-
|
799
|
+
|
808
800
|
# @see Psych.safe_load
|
809
801
|
def safe_load(*args,**kargs)
|
810
802
|
Psych.safe_load(*args,**kargs)
|
811
803
|
end
|
812
|
-
|
804
|
+
|
813
805
|
# @see Psych.to_json
|
814
806
|
def to_json(*args)
|
815
807
|
Psych.to_json(*args)
|
816
808
|
end
|
817
809
|
end
|
818
|
-
|
810
|
+
|
819
811
|
extend PsychDropIn
|
820
812
|
end
|