Class: Psychgus::SuperSniffer

Inherits:
Object
  • Object
show all
Defined in:
lib/psychgus/super_sniffer.rb,
lib/psychgus/super_sniffer.rb,
lib/psychgus/super_sniffer/parent.rb

Overview

Note:

You should never call the methods that are not readers, like #add_alias, #start_mapping, etc. unless you are extending this class (creating a subclass).

This is used in StyledTreeBuilder to "sniff" information about the YAML.

Then this information can be used in a Styler and/or a Blueberry.

Most information is straightforward:

  • #aliases # Array of Psych::Nodes::Alias processed so far
  • #documents # Array of Psych::Nodes::Document processed so far
  • #mappings # Array of Psych::Nodes::Mapping processed so far
  • #nodes # Array of Psych::Nodes::Node processed so far
  • #scalars # Array of Psych::Nodes::Scalar processed so far
  • #sequences # Array of Psych::Nodes::Sequence processed so far
  • #streams # Array of Psych::Nodes::Stream processed so far

#parent is the current Parent of the node being processed, which is an empty Parent for the first node (not nil).

#parents are all of the (grand)Parent(s) for the current node, which is an Array that just contains an empty Parent for the first node.

A parent is a Mapping or Sequence, or a Key (Scalar) in a Mapping.

#level and #position can be best understood by an example.

If you have this YAML: 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 - [Lettuce, Onions, Pickles, Tomatoes] - [[Ketchup,Mustard], [Salt,Pepper]]

Then the levels and positions will be as follows:

# (level:position):current_node - <parent:(parent_level:parent_position)>

(1:1):Psych::Nodes::Stream - <root:(0:0)>
(1:1):Psych::Nodes::Document - <stream:(1:1)>
(1:1):Psych::Nodes::Mapping - <doc:(1:1)>
(2:1):Burgers - <map:(1:1)>
(3:1):Psych::Nodes::Mapping - <Burgers:(2:1)>
 (4:1):Classic - <map:(3:1)>
  (5:1):Psych::Nodes::Mapping - <Classic:(4:1)>
   (6:1):Sauce - <map:(5:1)>
    (7:1):Psych::Nodes::Sequence - <Sauce:(6:1)>
     (8:1):Ketchup - <seq:(7:1)>
     (8:2):Mustard - <seq:(7:1)>
   (6:2):Cheese - <map:(5:1)>
    (7:1):American - <Cheese:(6:2)>
   (6:3):Bun - <map:(5:1)>
    (7:1):Sesame Seed - <Bun:(6:3)>
 (4:2):BBQ - <map:(3:1)>
  (5:1):Psych::Nodes::Mapping - <BBQ:(4:2)>
   (6:1):Sauce - <map:(5:1)>
    (7:1):Honey BBQ - <Sauce:(6:1)>
   (6:2):Cheese - <map:(5:1)>
    (7:1):Cheddar - <Cheese:(6:2)>
   (6:3):Bun - <map:(5:1)>
    (7:1):Kaiser - <Bun:(6:3)>
 (4:3):Fancy - <map:(3:1)>
  (5:1):Psych::Nodes::Mapping - <Fancy:(4:3)>
   (6:1):Sauce - <map:(5:1)>
    (7:1):Spicy Wasabi - <Sauce:(6:1)>
   (6:2):Cheese - <map:(5:1)>
    (7:1):Smoked Gouda - <Cheese:(6:2)>
   (6:3):Bun - <map:(5:1)>
    (7:1):Hawaiian - <Bun:(6:3)>
(2:2):Toppings - <map:(1:1)>
(3:1):Psych::Nodes::Sequence - <Toppings:(2:2)>
 (4:1):Mushrooms - <seq:(3:1)>
 (4:2):Psych::Nodes::Sequence - <seq:(3:1)>
  (5:1):Lettuce - <seq:(4:2)>
  (5:2):Onions - <seq:(4:2)>
  (5:3):Pickles - <seq:(4:2)>
  (5:4):Tomatoes - <seq:(4:2)>
 (4:3):Psych::Nodes::Sequence - <seq:(3:1)>
  (5:1):Psych::Nodes::Sequence - <seq:(4:3)>
   (6:1):Ketchup - <seq:(5:1)>
   (6:2):Mustard - <seq:(5:1)>
  (5:2):Psych::Nodes::Sequence - <seq:(4:3)>
   (6:1):Salt - <seq:(5:2)>
   (6:2):Pepper - <seq:(5:2)>

"The Super Sniffer" is the nickname for Gus's nose from the TV show Psych because he has a very refined sense of smell.

Direct Known Subclasses

Empty

Defined Under Namespace

Classes: Empty, Parent

Constant Summary collapse

EMPTY =
Empty.new.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize ⇒ SuperSniffer

Initialize this class for sniffing.



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/psychgus/super_sniffer.rb', line 151

def initialize
  @aliases = []
  @documents = []
  @level = 0
  @mappings = []
  @nodes = []
  @parent = nil
  @parents = []
  @position = 0
  @scalars = []
  @sequences = []
  @streams = []

  # Do not pass in "top_level: true"
  start_parent(nil,debug_tag: :root)
end

Instance Attribute Details

#aliases ⇒ Array<Psych::Nodes::Alias> (readonly)

Returns the aliases processed so far.

Returns:

  • (Array<Psych::Nodes::Alias>) —

    the aliases processed so far



138
139
140
# File 'lib/psychgus/super_sniffer.rb', line 138

def aliases
  @aliases
end

#documents ⇒ Array<Psych::Nodes::Document> (readonly)

Returns the documents processed so far.

Returns:

  • (Array<Psych::Nodes::Document>) —

    the documents processed so far



139
140
141
# File 'lib/psychgus/super_sniffer.rb', line 139

def documents
  @documents
end

#level ⇒ Integer (readonly)

Returns the current level.

Returns:

  • (Integer) —

    the current level



140
141
142
# File 'lib/psychgus/super_sniffer.rb', line 140

def level
  @level
end

#mappings ⇒ Array<Psych::Nodes::Mapping> (readonly)

Returns the mappings processed so far.

Returns:

  • (Array<Psych::Nodes::Mapping>) —

    the mappings processed so far



141
142
143
# File 'lib/psychgus/super_sniffer.rb', line 141

def mappings
  @mappings
end

#nodes ⇒ Array<Psych::Nodes::Node> (readonly)

Returns the nodes processed so far.

Returns:

  • (Array<Psych::Nodes::Node>) —

    the nodes processed so far



142
143
144
# File 'lib/psychgus/super_sniffer.rb', line 142

def nodes
  @nodes
end

#parent ⇒ Parent (readonly)

Returns the current parent.

Returns:

  • (Parent) —

    the current parent



143
144
145
# File 'lib/psychgus/super_sniffer.rb', line 143

def parent
  @parent
end

#parents ⇒ Array<Parent> (readonly)

Returns the current (grand)parents.

Returns:

  • (Array<Parent>) —

    the current (grand)parents



144
145
146
# File 'lib/psychgus/super_sniffer.rb', line 144

def parents
  @parents
end

#position ⇒ Integer (readonly)

Returns the current position.

Returns:

  • (Integer) —

    the current position



145
146
147
# File 'lib/psychgus/super_sniffer.rb', line 145

def position
  @position
end

#scalars ⇒ Array<Psych::Nodes::Scalar> (readonly)

Returns the scalars processed so far.

Returns:

  • (Array<Psych::Nodes::Scalar>) —

    the scalars processed so far



146
147
148
# File 'lib/psychgus/super_sniffer.rb', line 146

def scalars
  @scalars
end

#sequences ⇒ Array<Psych::Nodes::Sequence> (readonly)

Returns the sequences processed so far.

Returns:

  • (Array<Psych::Nodes::Sequence>) —

    the sequences processed so far



147
148
149
# File 'lib/psychgus/super_sniffer.rb', line 147

def sequences
  @sequences
end

#streams ⇒ Array<Psych::Nodes::Stream> (readonly)

Returns the streams processed so far.

Returns:

  • (Array<Psych::Nodes::Stream>) —

    the streams processed so far



148
149
150
# File 'lib/psychgus/super_sniffer.rb', line 148

def streams
  @streams
end

Instance Method Details

#add_alias(node) ⇒ Object

Add a Psych::Nodes::Alias to this class only (not to the YAML).

A Psychgus::Styler should probably never call this.

Parameters:

  • node (Psych::Nodes::Alias) —

    the alias to add

See Also:



175
176
177
178
# File 'lib/psychgus/super_sniffer.rb', line 175

def add_alias(node)
  add_child(node)
  @aliases.push(node)
end

#add_child(node) ⇒ Object (protected)

Add a non-parent node.

This will increment #position accordingly, and if the child is a Key to a Mapping, create a fake "Parent".

Parameters:

  • node (Psych::Nodes::Node) —

    the non-parent Node to add

See Also:



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/psychgus/super_sniffer.rb', line 306

def add_child(node)
  if !@parent.nil?
    # Fake a "parent" if necessary
    case @parent.child_type
    when :key
      start_mapping_key(node)
      return
    when :value
      end_mapping_value
      return
    else
      @parent.child_position += 1
    end
  end

  @position += 1

  @nodes.push(node)
end

#add_scalar(node) ⇒ Object

Add a Psych::Nodes::Scalar to this class only (not to the YAML).

A Psychgus::Styler should probably never call this.

Parameters:

  • node (Psych::Nodes::Scalar) —

    the scalar to add

See Also:



187
188
189
190
# File 'lib/psychgus/super_sniffer.rb', line 187

def add_scalar(node)
  add_child(node)
  @scalars.push(node)
end

#end_document ⇒ Object

End a Psych::Nodes::Document started with #start_document.

Pops off a parent from #parents and sets #parent to the last one. #level and #position are reset according to the last parent.

A Psychgus::Styler should probably never call this.



198
199
200
# File 'lib/psychgus/super_sniffer.rb', line 198

def end_document
  end_parent(top_level: true)
end

#end_mapping ⇒ Object

End a Psych::Nodes::Mapping started with #start_mapping.

Pops off a parent from #parents and sets #parent to the last one. #level and #position are reset according to the last parent.

A Psychgus::Styler should probably never call this.

See Also:



210
211
212
# File 'lib/psychgus/super_sniffer.rb', line 210

def end_mapping
  end_parent(mapping_value: true)
end

#end_mapping_value ⇒ Object (protected)

End a fake "Parent" that is a Key/Value to a Mapping.

See Also:



329
330
331
332
333
# File 'lib/psychgus/super_sniffer.rb', line 329

def end_mapping_value
  end_parent # Do not pass in "mapping_value: true" and/or "top_level: true"

  @parent.child_type = :key unless @parent.nil?
end

#end_parent(mapping_value: false, top_level: false) ⇒ Object (protected)

End a Parent.

Pops off a parent from #parents and sets #parent to the last one. #level and #position are reset according to the last parent.

Parameters:

  • mapping_value (true, false) (defaults to: false) —

    true if parent can be the value of a Mapping's key

  • top_level (true, false) (defaults to: false) —

    true if a top-level parent (i.e., encapsulating the main data)



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/psychgus/super_sniffer.rb', line 342

def end_parent(mapping_value: false,top_level: false)
  @parents.pop
  @parent = @parents.last

  @level = top_level ? 1 : (@level - 1)

  if !@parent.nil?
    @parent.child_position += 1
    @position = @parent.child_position

    # add_child() will not be called again, so end a fake "parent" manually with a fake "value"
    # - This is necessary for any parents that can be the value of a map's key (e.g., Sequence)
    end_mapping_value if mapping_value && !@parent.child_type.nil?
  end
end

#end_sequence ⇒ Object

End a Psych::Nodes::Sequence started with #start_sequence.

Pops off a parent from #parents and sets #parent to the last one. #level and #position are reset according to the last parent.

A Psychgus::Styler should probably never call this.

See Also:



222
223
224
# File 'lib/psychgus/super_sniffer.rb', line 222

def end_sequence
  end_parent(mapping_value: true)
end

#end_stream ⇒ Object

End a Psych::Nodes::Stream started with #start_stream.

Pops off a parent from #parents and sets #parent to the last one. #level and #position are reset according to the last parent.

A Psychgus::Styler should probably never call this.



232
233
234
# File 'lib/psychgus/super_sniffer.rb', line 232

def end_stream
  end_parent(top_level: true)
end

#start_document(node) ⇒ Object

Start a Psych::Nodes::Document.

Creates a Parent, sets #parent to it, and adds it to #parents. #level and #position are incremented/set accordingly.

A Psychgus::Styler should probably never call this.

Parameters:

  • node (Psych::Nodes::Document) —

    the Document to start

See Also:



246
247
248
249
# File 'lib/psychgus/super_sniffer.rb', line 246

def start_document(node)
  start_parent(node,debug_tag: :doc,top_level: true)
  @documents.push(node)
end

#start_mapping(node) ⇒ Object

Start a Psych::Nodes::Mapping.

Creates a Parent, sets #parent to it, and adds it to #parents. #level and #position are incremented/set accordingly.

A Psychgus::Styler should probably never call this.

Parameters:

  • node (Psych::Nodes::Mapping) —

    the Mapping to start

See Also:



261
262
263
264
# File 'lib/psychgus/super_sniffer.rb', line 261

def start_mapping(node)
  start_parent(node,debug_tag: :map,child_type: :key)
  @mappings.push(node)
end

#start_mapping_key(node) ⇒ Object (protected)

Start a fake "Parent" that is a Key/Value to a Mapping.

Creates a Parent, sets #parent to it, and adds it to #parents. #level and #position are incremented/set accordingly.

Parameters:

  • node (Psych::Nodes::Node) —

    the Node to start

See Also:



366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/psychgus/super_sniffer.rb', line 366

def start_mapping_key(node)
  debug_tag = nil

  # Value must be first because Scalar also has an anchor
  if node.respond_to?(:value)
    debug_tag = node.value
  elsif node.respond_to?(:anchor)
    debug_tag = node.anchor
  end

  debug_tag = :noface if debug_tag.nil?

  start_parent(node,debug_tag: debug_tag,child_type: :value)
end

#start_parent(node, top_level: false, **extra) ⇒ Object (protected)

Start a Parent.

Creates a Parent, sets #parent to it, and adds it to #parents. #level and #position are incremented/set accordingly.

Parameters:

  • node (Psych::Nodes::Node) —

    the parent Node to start

  • top_level (true, false) (defaults to: false) —

    true if a top-level parent (i.e., encapsulating the main data)

  • extra (Hash) —

    the extra keyword args to pass to Psychgus::SuperSniffer::Parent#initialize

See Also:



391
392
393
394
395
396
397
398
399
400
401
402
403
404
# File 'lib/psychgus/super_sniffer.rb', line 391

def start_parent(node,top_level: false,**extra)
  @parent = Parent.new(self,node,**extra)

  @parents.push(@parent)
  @nodes.push(node) unless node.nil?

  if top_level
    @level = 1
    @position = @parent.position
  else
    @level += 1
    @position = 1
  end
end

#start_sequence(node) ⇒ Object

Start a Psych::Nodes::Sequence.

Creates a Parent, sets #parent to it, and adds it to #parents. #level and #position are incremented/set accordingly.

A Psychgus::Styler should probably never call this.

Parameters:

  • node (Psych::Nodes::Sequence) —

    the Sequence to start

See Also:



276
277
278
279
# File 'lib/psychgus/super_sniffer.rb', line 276

def start_sequence(node)
  start_parent(node,debug_tag: :seq)
  @sequences.push(node)
end

#start_stream(node) ⇒ Object

Start a Psych::Nodes::Stream.

Creates a Parent, sets #parent to it, and adds it to #parents. #level and #position are incremented/set accordingly.

A Psychgus::Styler should probably never call this.

Parameters:

  • node (Psych::Nodes::Stream) —

    the Stream to start

See Also:



291
292
293
294
# File 'lib/psychgus/super_sniffer.rb', line 291

def start_stream(node)
  start_parent(node,debug_tag: :stream,top_level: true)
  @streams.push(node)
end