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.
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ #--
5
+ # This file is part of Psychgus.
6
+ # Copyright (c) 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/styler'
26
+ require 'psychgus/super_sniffer'
27
+
28
+ module Psychgus
29
+ ###
30
+ # Use this wherever Psych::TreeBuilder would have been used, to enable styling.
31
+ #
32
+ # @author Jonathan Bradley Whited (@esotericpig)
33
+ # @since 1.0.0
34
+ #
35
+ # @see Psychgus.parser Psychgus.parser
36
+ # @see Psychgus.dump_stream Psychgus.dump_stream
37
+ # @see Psych::TreeBuilder
38
+ ###
39
+ class StyledTreeBuilder < Psych::TreeBuilder
40
+ # @return [true,false] whether to dereference aliases; output the actual value instead of the alias
41
+ attr_accessor :deref_aliases
42
+ alias_method :deref_aliases?,:deref_aliases
43
+
44
+ # @return [SuperSniffer] the {SuperSniffer} being used to sniff the YAML nodes, level, etc.
45
+ attr_reader :sniffer
46
+
47
+ # @return [Array<Stylers>] the {Styler}(s) being used to style the YAML nodes
48
+ attr_reader :stylers
49
+
50
+ # Initialize this class with {Styler}(s).
51
+ #
52
+ # @param stylers [Styler] {Styler}(s) to use for styling this TreeBuilder
53
+ # @param deref_aliases [true,false] whether to dereference aliases; output the actual value
54
+ # instead of the alias
55
+ def initialize(*stylers,deref_aliases: false,**options)
56
+ super()
57
+
58
+ @deref_aliases = deref_aliases
59
+ @sniffer = SuperSniffer.new()
60
+ @stylers = []
61
+
62
+ add_styler(*stylers)
63
+ end
64
+
65
+ # Add {Styler}(s) onto the end of the data structure.
66
+ #
67
+ # @param stylers [Styler] {Styler}(s) to add
68
+ #
69
+ # @return [self] this class
70
+ def add_styler(*stylers)
71
+ @stylers.push(*stylers)
72
+
73
+ return self
74
+ end
75
+
76
+ # Calls super, styler(s), and sniffer.
77
+ #
78
+ # @see Psych::TreeBuilder#alias
79
+ # @see Styler#style
80
+ # @see Styler#style_alias
81
+ # @see SuperSniffer#add_alias
82
+ #
83
+ # @return [Psych::Nodes::Alias] the alias node created
84
+ def alias(*)
85
+ node = super
86
+
87
+ @stylers.each do |styler|
88
+ styler.style(sniffer,node)
89
+ styler.style_alias(sniffer,node)
90
+ end
91
+
92
+ @sniffer.add_alias(node)
93
+
94
+ return node
95
+ end
96
+
97
+ # Calls super and sniffer.
98
+ #
99
+ # @see Psych::TreeBuilder#end_document
100
+ # @see SuperSniffer#end_document
101
+ def end_document(*)
102
+ result = super
103
+
104
+ @sniffer.end_document()
105
+
106
+ return result
107
+ end
108
+
109
+ # Calls super and sniffer.
110
+ #
111
+ # @see Psych::TreeBuilder#end_mapping
112
+ # @see SuperSniffer#end_mapping
113
+ def end_mapping(*)
114
+ result = super
115
+
116
+ @sniffer.end_mapping()
117
+
118
+ return result
119
+ end
120
+
121
+ # Calls super and sniffer.
122
+ #
123
+ # @see Psych::TreeBuilder#end_sequence
124
+ # @see SuperSniffer#end_sequence
125
+ def end_sequence(*)
126
+ result = super
127
+
128
+ @sniffer.end_sequence()
129
+
130
+ return result
131
+ end
132
+
133
+ # Calls super and sniffer.
134
+ #
135
+ # @see Psych::TreeBuilder#end_stream
136
+ # @see SuperSniffer#end_stream
137
+ def end_stream(*)
138
+ result = super
139
+
140
+ @sniffer.end_stream()
141
+
142
+ return result
143
+ end
144
+
145
+ # Insert {Styler}(s) at +index+ into the data structure.
146
+ #
147
+ # @param stylers [Styler] {Styler}(s) to insert
148
+ #
149
+ # @return [self] this class
150
+ def insert_styler(index,*stylers)
151
+ @stylers.insert(index,*stylers)
152
+
153
+ return self
154
+ end
155
+
156
+ # Remove the last {Styler}(s) from the data structure.
157
+ #
158
+ # @param count [Integer] the optional amount of tail elements to pop
159
+ #
160
+ # @return [Styler,Array<Styler>,nil] the last {Styler}(s), or if empty or count==0, nil
161
+ def pop_styler(count=1)
162
+ return nil if count == 0
163
+ return @stylers.pop() if count == 1
164
+
165
+ return @stylers.pop(count)
166
+ end
167
+
168
+ # Remove the {Styler} that matches +styler+ from the data structure.
169
+ #
170
+ # An optional +block+ can return a default value if not found.
171
+ #
172
+ # @param styler [Styler] the {Styler} to find and remove
173
+ # @param block [Proc] an optional block to call when +styler+ is not found
174
+ #
175
+ # @return [Styler,nil] the last {Styler}, or if not found, nil or the result of +block+
176
+ def remove_styler(styler,&block)
177
+ return @stylers.delete(styler,&block)
178
+ end
179
+
180
+ # Remove the {Styler} at +index+ from the data structure.
181
+ #
182
+ # @param index [Integer] the index of the {Styler} to remove
183
+ #
184
+ # @return [Styler,nil] the {Styler} removed or nil if empty
185
+ def remove_styler_at(index)
186
+ return @stylers.delete_at(index)
187
+ end
188
+
189
+ # Calls super, styler(s), and sniffer.
190
+ #
191
+ # @see Psych::TreeBuilder#scalar
192
+ # @see Styler#style
193
+ # @see Styler#style_scalar
194
+ # @see SuperSniffer#add_scalar
195
+ #
196
+ # @return [Psych::Nodes::Scalar] the scalar node created
197
+ def scalar(*)
198
+ node = super
199
+
200
+ @stylers.each do |styler|
201
+ styler.style(sniffer,node)
202
+ styler.style_scalar(sniffer,node)
203
+ end
204
+
205
+ @sniffer.add_scalar(node)
206
+
207
+ return node
208
+ end
209
+
210
+ # Calls super, styler(s), and sniffer.
211
+ #
212
+ # @see Psych::TreeBuilder#start_document
213
+ # @see Styler#style
214
+ # @see Styler#style_document
215
+ # @see SuperSniffer#start_document
216
+ #
217
+ # @return [Psych::Nodes::Document] the document node created
218
+ def start_document(*)
219
+ node = super
220
+
221
+ @stylers.each do |styler|
222
+ styler.style(sniffer,node)
223
+ styler.style_document(sniffer,node)
224
+ end
225
+
226
+ @sniffer.start_document(node)
227
+
228
+ return node
229
+ end
230
+
231
+ # Calls super, styler(s), and sniffer.
232
+ #
233
+ # @see Psych::TreeBuilder#start_mapping
234
+ # @see Styler#style
235
+ # @see Styler#style_mapping
236
+ # @see SuperSniffer#start_mapping
237
+ #
238
+ # @return [Psych::Nodes::Mapping] the mapping node created
239
+ def start_mapping(*)
240
+ node = super
241
+
242
+ @stylers.each do |styler|
243
+ styler.style(sniffer,node)
244
+ styler.style_mapping(sniffer,node)
245
+ end
246
+
247
+ @sniffer.start_mapping(node)
248
+
249
+ return node
250
+ end
251
+
252
+ # Calls super, styler(s), and sniffer.
253
+ #
254
+ # @see Psych::TreeBuilder#start_sequence
255
+ # @see Styler#style
256
+ # @see Styler#style_sequence
257
+ # @see SuperSniffer#start_sequence
258
+ #
259
+ # @return [Psych::Nodes::Sequence] the sequence node created
260
+ def start_sequence(*)
261
+ node = super
262
+
263
+ @stylers.each do |styler|
264
+ styler.style(sniffer,node)
265
+ styler.style_sequence(sniffer,node)
266
+ end
267
+
268
+ @sniffer.start_sequence(node)
269
+
270
+ return node
271
+ end
272
+
273
+ # Calls super, styler(s), and sniffer.
274
+ #
275
+ # @see Psych::TreeBuilder#start_stream
276
+ # @see Styler#style
277
+ # @see Styler#style_stream
278
+ # @see SuperSniffer#start_stream
279
+ #
280
+ # @return [Psych::Nodes::Stream] the stream node created
281
+ def start_stream(*)
282
+ node = super
283
+
284
+ @stylers.each do |styler|
285
+ styler.style(sniffer,node)
286
+ styler.style_stream(sniffer,node)
287
+ end
288
+
289
+ @sniffer.start_stream(node)
290
+
291
+ return node
292
+ end
293
+ end
294
+ end
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ #--
5
+ # This file is part of Psychgus.
6
+ # Copyright (c) 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 'psychgus/super_sniffer'
24
+
25
+ module Psychgus
26
+ module Styler
27
+ ###
28
+ # An empty {Styler} as a class.
29
+ #
30
+ # @author Jonathan Bradley Whited (@esotericpig)
31
+ # @since 1.0.0
32
+ ###
33
+ class Empty
34
+ include Styler
35
+ end
36
+ end
37
+
38
+ ###
39
+ # Mix in (include) this module to make a class/module/etc. a styler for YAML.
40
+ #
41
+ # Although it's unnecessary (because of Duck Typing), it's the recommended practice in case a new method is
42
+ # added in the future, and also so you don't have to define methods that you don't use.
43
+ #
44
+ # You can either use this as is (see example) or inside of a class (see {Blueberry}).
45
+ #
46
+ # @example
47
+ # class MyStyler
48
+ # include Psychgus::Styler
49
+ #
50
+ # def style_sequence(sniffer,node)
51
+ # node.style = Psychgus::SEQUENCE_FLOW if sniffer.level == 3
52
+ # end
53
+ # end
54
+ #
55
+ # hash = {'Coffee'=>{
56
+ # 'Roast'=>['Light','Medium','Dark','Extra Dark'],
57
+ # 'Style'=>['Cappuccino','Espresso','Latte','Mocha']
58
+ # }}
59
+ # puts hash.to_yaml(stylers: MyStyler.new())
60
+ #
61
+ # # Output:
62
+ # # ---
63
+ # # Coffee:
64
+ # # Roast: [Light, Medium, Dark, Extra Dark]
65
+ # # Style: [Cappuccino, Espresso, Latte, Mocha]
66
+ #
67
+ # @author Jonathan Bradley Whited (@esotericpig)
68
+ # @since 1.0.0
69
+ #
70
+ # @see Psychgus
71
+ # @see Ext::ObjectExt#to_yaml
72
+ # @see Blueberry
73
+ # @see StyledTreeBuilder#initialize
74
+ # @see StyledDocumentStream#initialize
75
+ # @see Ext::YAMLTreeExt#accept
76
+ ###
77
+ module Styler
78
+ EMPTY = Empty.new().freeze()
79
+
80
+ # Style a node of any type.
81
+ #
82
+ # You can use {Ext::NodeExt#node_of?} to determine its type:
83
+ # puts node.value if node.node_of?(:scalar)
84
+ #
85
+ # @param sniffer [SuperSniffer] passed in from {StyledTreeBuilder}
86
+ # @param node [Psych::Nodes::Node] passed in from {StyledTreeBuilder}
87
+ #
88
+ # @see Ext::NodeExt#node_of?
89
+ def style(sniffer,node) end
90
+
91
+ # Style a node guaranteed to be of type Psych::Nodes::Alias, to avoid if statements.
92
+ #
93
+ # @param sniffer [SuperSniffer] passed in from {StyledTreeBuilder}
94
+ # @param node [Psych::Nodes::Alias] of type Alias passed in from {StyledTreeBuilder}
95
+ def style_alias(sniffer,node) end
96
+
97
+ # Style a node guaranteed to be of type Psych::Nodes::Document, to avoid if statements.
98
+ #
99
+ # @param sniffer [SuperSniffer] passed in from {StyledTreeBuilder}
100
+ # @param node [Psych::Nodes::Document] of type Document passed in from {StyledTreeBuilder}
101
+ def style_document(sniffer,node) end
102
+
103
+ # Style a node guaranteed to be of type Psych::Nodes::Mapping, to avoid if statements.
104
+ #
105
+ # @param sniffer [SuperSniffer] passed in from {StyledTreeBuilder}
106
+ # @param node [Psych::Nodes::Mapping] of type Mapping passed in from {StyledTreeBuilder}
107
+ def style_mapping(sniffer,node) end
108
+
109
+ # Style a node guaranteed to be of type Psych::Nodes::Scalar, to avoid if statements.
110
+ #
111
+ # @param sniffer [SuperSniffer] passed in from {StyledTreeBuilder}
112
+ # @param node [Psych::Nodes::Scalar] of type Scalar passed in from {StyledTreeBuilder}
113
+ def style_scalar(sniffer,node) end
114
+
115
+ # Style a node guaranteed to be of type Psych::Nodes::Sequence, to avoid if statements.
116
+ #
117
+ # @param sniffer [SuperSniffer] passed in from {StyledTreeBuilder}
118
+ # @param node [Psych::Nodes::Sequence] of type Sequence passed in from {StyledTreeBuilder}
119
+ def style_sequence(sniffer,node) end
120
+
121
+ # Style a node guaranteed to be of type Psych::Nodes::Stream, to avoid if statements.
122
+ #
123
+ # @param sniffer [SuperSniffer] passed in from {StyledTreeBuilder}
124
+ # @param node [Psych::Nodes::Stream] of type Stream passed in from {StyledTreeBuilder}
125
+ def style_stream(sniffer,node) end
126
+ end
127
+ end
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ #--
5
+ # This file is part of Psychgus.
6
+ # Copyright (c) 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
+ module Psychgus
24
+ class SuperSniffer
25
+ ###
26
+ # A container for the parent of a Psych::Nodes::Node.
27
+ #
28
+ # A parent is a Mapping, Sequence, or a Key (Scalar) of a Mapping.
29
+ #
30
+ # You can use the getters in this class in {Styler} to filter what to change.
31
+ #
32
+ # If a Node method has not been exposed, you can use {#node}:
33
+ # if parent.node_of?(:scalar)
34
+ # parent.value = 'FUBAR'
35
+ # parent.node.value = 'FUBAR' # Same as above
36
+ #
37
+ # parent.fubar = true # NoMethodError
38
+ # parent.node.fubar = true # Use some new Psych::Nodes::Node method not in this version
39
+ # # of Psychgus or that is not exposed by Parent
40
+ # end
41
+ #
42
+ # @author Jonathan Bradley Whited (@esotericpig)
43
+ # @since 1.0.0
44
+ #
45
+ # @see SuperSniffer
46
+ # @see SuperSniffer#start_parent SuperSniffer#start_parent
47
+ # @see SuperSniffer#end_parent SuperSniffer#end_parent
48
+ # @see Styler
49
+ ###
50
+ class Parent
51
+ # Calling the getter is fine; calling the setter is *not* and could cause weird results.
52
+ #
53
+ # @return [Integer] the next child's position
54
+ attr_accessor :child_position
55
+
56
+ # Calling the getter is fine; calling the setter is *not* and could cause weird results.
57
+ #
58
+ # @return [nil,:key,:value] the next child's Mapping type, if {#node} is a Mapping
59
+ attr_accessor :child_type
60
+
61
+ # @return [:noface,Symbol,String] a tag (class name, value) for debugging; also used in {#to_s}
62
+ attr_reader :debug_tag
63
+
64
+ attr_reader :level # @return [Integer] the level of this Node in the YAML
65
+ attr_reader :node # @return [Psych::Nodes::Node] the Node of this parent
66
+ attr_reader :position # @return [Integer] the position of this Node in the YAML
67
+
68
+ # Initialize this class with parent data.
69
+ #
70
+ # @param sniffer [SuperSniffer] the sniffer that contains this parent (not stored; used for data)
71
+ # @param node [Psych::Nodes::Node] the node of this parent
72
+ # @param debug_tag [:noface,Symbol,String] the tag (class name, value) used for debugging and in {#to_s}
73
+ # @param child_type [nil,:key,:value] the next child's Mapping type, if +node+ is a Mapping
74
+ def initialize(sniffer,node,debug_tag: nil,child_type: nil)
75
+ @child_position = 1
76
+ @child_type = child_type
77
+ @debug_tag = debug_tag
78
+ @level = sniffer.level
79
+ @node = node
80
+ @position = sniffer.position
81
+ end
82
+
83
+ # @see Psych::Nodes::Alias#anchor=
84
+ # @see Psych::Nodes::Mapping#anchor=
85
+ # @see Psych::Nodes::Scalar#anchor=
86
+ # @see Psych::Nodes::Sequence#anchor=
87
+ def anchor=(anchor)
88
+ node.anchor = anchor
89
+ end
90
+
91
+ # @see Psych::Nodes::Scalar#plain=
92
+ def plain=(plain)
93
+ node.plain = plain
94
+ end
95
+
96
+ # @see Psych::Nodes::Scalar#quoted=
97
+ def quoted=(quoted)
98
+ node.quoted = quoted
99
+ end
100
+
101
+ # @see Psych::Nodes::Mapping#style=
102
+ # @see Psych::Nodes::Scalar#style=
103
+ # @see Psych::Nodes::Sequence#style=
104
+ def style=(style)
105
+ node.style = style
106
+ end
107
+
108
+ # @see Psych::Nodes::Node#tag=
109
+ def tag=(tag)
110
+ node.tag = tag
111
+ end
112
+
113
+ # @see Psych::Nodes::Scalar#value=
114
+ def value=(value)
115
+ node.value = value
116
+ end
117
+
118
+ # @see Psych::Nodes::Alias#anchor
119
+ # @see Psych::Nodes::Mapping#anchor
120
+ # @see Psych::Nodes::Scalar#anchor
121
+ # @see Psych::Nodes::Sequence#anchor
122
+ def anchor()
123
+ return node.anchor
124
+ end
125
+
126
+ # @see Psych::Nodes::Stream#encoding
127
+ def encoding()
128
+ return node.encoding
129
+ end
130
+
131
+ # @see Psych::Nodes::Node#end_column
132
+ def end_column()
133
+ return node.end_column
134
+ end
135
+
136
+ # @see Psych::Nodes::Node#end_line
137
+ def end_line()
138
+ return node.end_line
139
+ end
140
+
141
+ # @see Psych::Nodes::Document#implicit
142
+ # @see Psych::Nodes::Mapping#implicit
143
+ # @see Psych::Nodes::Sequence#implicit
144
+ def implicit?()
145
+ return node.implicit
146
+ end
147
+
148
+ # @see Psych::Nodes::Document#implicit_end
149
+ def implicit_end?()
150
+ return node.implicit_end
151
+ end
152
+
153
+ # (see Ext::NodeExt#node_of?)
154
+ def node_of?(*names)
155
+ return node.node_of?(*names)
156
+ end
157
+
158
+ # @see Psych::Nodes::Scalar#plain
159
+ def plain?()
160
+ return node.plain
161
+ end
162
+
163
+ # @see Psych::Nodes::Scalar#quoted
164
+ def quoted?()
165
+ return node.quoted
166
+ end
167
+
168
+ # @see Psych::Nodes::Node#start_column
169
+ def start_column()
170
+ return node.start_column
171
+ end
172
+
173
+ # @see Psych::Nodes::Node#start_line
174
+ def start_line()
175
+ return node.start_line
176
+ end
177
+
178
+ # @see Psych::Nodes::Mapping#style
179
+ # @see Psych::Nodes::Scalar#style
180
+ # @see Psych::Nodes::Sequence#style
181
+ def style()
182
+ return node.style
183
+ end
184
+
185
+ # @see Psych::Nodes::Node#tag
186
+ def tag()
187
+ return node.tag
188
+ end
189
+
190
+ # @see Psych::Nodes::Document#tag_directives
191
+ def tag_directives()
192
+ return node.tag_directives
193
+ end
194
+
195
+ # @see Psych::Nodes::Scalar#value
196
+ def value()
197
+ return node.value
198
+ end
199
+
200
+ # @see Psych::Nodes::Document#version
201
+ def version()
202
+ return node.version
203
+ end
204
+
205
+ # @note If this method is modified, then tests will fail
206
+ #
207
+ # @return [String] a String representation of this class for debugging and testing
208
+ def to_s()
209
+ return "<#{@debug_tag}:(#{@level}:#{@position}):#{@child_type}:(:#{@child_position})>"
210
+ end
211
+
212
+ alias_method :implicit,:implicit?
213
+ alias_method :implicit_end,:implicit_end?
214
+ alias_method :plain,:plain?
215
+ alias_method :quoted,:quoted?
216
+ end
217
+ end
218
+ end