Class: Kitchen::ElementBase

Inherits:
Object show all
Extended by:
Forwardable
Includes:
Mixins::BlockErrorIf
Defined in:
lib/kitchen/element_base.rb

Overview

Abstract base class for all elements. If you are looking for a simple concrete element class, use ‘Element`.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Mixins::BlockErrorIf

#block_error_if

Constructor Details

#initialize(node:, document:, enumerator_class:, short_type: nil) ⇒ ElementBase

Creates a new instance

Parameters:

Raises:

  • (ArgumentError)


120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/kitchen/element_base.rb', line 120

def initialize(node:, document:, enumerator_class:, short_type: nil)
  raise(ArgumentError, 'node cannot be nil') if node.nil?

  @node = node

  raise(ArgumentError, 'enumerator_class cannot be nil') if enumerator_class.nil?

  @enumerator_class = enumerator_class

  @short_type = short_type ||
                self.class.try(:short_type) ||
                "unknown_type_#{SecureRandom.hex(4)}"

  @document =
    case document
    when Kitchen::Document
      document
    else
      raise(ArgumentError, '`document` is not a known document type')
    end

  @ancestors = HashWithIndifferentAccess.new
  @search_query_matches_that_have_been_counted = {}
  @is_a_clone = false
  @search_cache = {}
end

Instance Attribute Details

#ancestorsArray<Ancestor> (readonly)

Returns the element’s ancestors

Returns:



283
284
285
# File 'lib/kitchen/element_base.rb', line 283

def ancestors
  @ancestors
end

#document(: clipboard) ⇒ Clipboard (readonly)

Access the clipboard for this element’s document

Returns:



17
18
19
# File 'lib/kitchen/element_base.rb', line 17

def document
  @document
end

#enumerator_classClass (readonly)

The enumerator class for this element

Returns:

  • (Class)


25
26
27
# File 'lib/kitchen/element_base.rb', line 25

def enumerator_class
  @enumerator_class
end

#search_query_that_found_meSearchQuery

The search query that located this element in the DOM

Returns:



29
30
31
# File 'lib/kitchen/element_base.rb', line 29

def search_query_that_found_me
  @search_query_that_found_me
end

#short_typeSymbol, String (readonly)

The element’s type, e.g. :page

Returns:



21
22
23
# File 'lib/kitchen/element_base.rb', line 21

def short_type
  @short_type
end

Class Method Details

.descendant(type) ⇒ Class

Returns ElementBase descendent type or nil if none found

Parameters:

  • type (Symbol)

    the descendant type, e.g. ‘:page`

Returns:

  • (Class)

    the child class for the given type



152
153
154
155
156
157
158
159
160
161
# File 'lib/kitchen/element_base.rb', line 152

def self.descendant(type)
  @types_to_descendants ||=
    descendants.each_with_object({}) do |descendant, hash|
      next unless descendant.try(:short_type)

      hash[descendant.short_type] = descendant
    end

  @types_to_descendants[type]
end

.descendant!(type) ⇒ Class

Returns ElementBase descendent type or Error if none found

Parameters:

  • type (Symbol)

    the descendant type, e.g. ‘:page`

Returns:

  • (Class)

    the child class for the given type

Raises:

  • if the type is unknown



169
170
171
# File 'lib/kitchen/element_base.rb', line 169

def self.descendant!(type)
  descendant(type) || raise("Unknown ElementBase descendant type '#{type}'")
end

.is_the_element_class_for?(node, config:) ⇒ Boolean

Returns true if this class represents the element for the given node

Parameters:

Returns:

  • (Boolean)


189
190
191
# File 'lib/kitchen/element_base.rb', line 189

def self.is_the_element_class_for?(node, config:)
  Selector.named(short_type).matches?(node, config: config)
end

Instance Method Details

#[]String

Get an element attribute

Returns:

See Also:



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#[]=Object

Set an element attribute



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#add_ancestor(ancestor) ⇒ Object

Adds one ancestor, incrementing its descendant counts for this element type

Parameters:

Raises:

  • (StandardError)

    if there is already an ancestor with the given ancestor’s type



311
312
313
314
315
316
317
318
319
# File 'lib/kitchen/element_base.rb', line 311

def add_ancestor(ancestor)
  if @ancestors[ancestor.type].present?
    raise "Trying to add an ancestor of type '#{ancestor.type}' but one of that " \
          "type is already present"
  end

  ancestor.increment_descendant_count(short_type)
  @ancestors[ancestor.type] = ancestor
end

#add_ancestors(*args) ⇒ Object

Adds ancestors to this element, for each incrementing descendant counts for this type

Parameters:

Raises:

  • (StandardError)

    if there is already an ancestor with the one of the given ancestors’ types



291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/kitchen/element_base.rb', line 291

def add_ancestors(*args)
  args.each do |arg|
    case arg
    when Hash
      add_ancestors(*arg.values)
    when Ancestor
      add_ancestor(arg)
    when Element, Document
      add_ancestor(Ancestor.new(arg))
    else
      raise "Unsupported ancestor type `#{arg.class}`"
    end
  end
end

#add_classObject

Add a class to the element



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#ancestor(type) ⇒ Ancestor

Returns this element’s ancestor of the given type

Parameters:

  • type (String, Symbol)

    e.g. :page, :term

Returns:

Raises:

  • (StandardError)

    if there is no ancestor of the given type



266
267
268
# File 'lib/kitchen/element_base.rb', line 266

def ancestor(type)
  @ancestors[type.to_sym]&.element || raise("No ancestor of type '#{type}'")
end

#ancestor_elementsArray<ElementBase>

Return the elements in all of the ancestors

Returns:



325
326
327
# File 'lib/kitchen/element_base.rb', line 325

def ancestor_elements
  @ancestors.values.map(&:element)
end

#append(child: nil, sibling: nil) ⇒ Object

If child argument given, appends it after the element’s current children. If sibling is given, appends it as a sibling to this element.

Parameters:

  • child (String) (defaults to: nil)

    the child to append

  • sibling (String) (defaults to: nil)

    the sibling to append

Raises:

  • (RecipeError)

    if specify other than just a child or a sibling



572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
# File 'lib/kitchen/element_base.rb', line 572

def append(child: nil, sibling: nil)
  require_one_of_child_or_sibling(child, sibling)

  if child
    if node.children.empty?
      node.children = child.to_s
    else
      node.add_child(child)
    end
  else
    node.next = sibling
  end

  self
end

#as_enumeratorElementEnumeratorBase

Returns this element as an enumerator (over only one element, itself)

Returns:



783
784
785
# File 'lib/kitchen/element_base.rb', line 783

def as_enumerator
  enumerator_class.new(search_query: search_query_that_found_me) { |block| block.yield(self) }
end

#childrenNokogiri::XML::NodeSet

Get the element’s children

Returns:

  • (Nokogiri::XML::NodeSet)

See Also:



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#classesArray<String>

Gets the element’s classes

Returns:

See Also:



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#cloneObject

Returns a clone of this object



713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
# File 'lib/kitchen/element_base.rb', line 713

def clone
  super.tap do |element|
    # When we call dup, the dup gets a bunch of default namespace stuff that
    # the original doesn't have.  Why? Unclear, but hard to get rid of nicely.
    # So here we mark that the element is a clone and then all of the `to_s`-like
    # methods gsub out the default namespace gunk.  Clones are mostly used for
    # clipboards and are accessed using `paste` methods, so modifying the `to_s`
    # behavior works for us.  If we end up using `clone` in a way that doesn't
    # eventually get converted to string, we may have to investigate other
    # options.
    #
    # An alternative is to remove the `xmlns` attribute in the `html` tag before
    # the input file is parse into a Nokogiri document and then to add it back
    # in when the baked file is written out.
    #
    # Nokogiri::XML::Document.remove_namespaces! is not an option because that blows
    # away our MathML namespace.
    #
    # I may not fully understand why the extra default namespace stuff is happening
    # FWIW :-)
    #
    element.node = node.dup
    element.is_a_clone = true
  end
end

#configConfig

Get the config for this element’s document

Returns:



96
# File 'lib/kitchen/element_base.rb', line 96

def_delegators :document, :config

#contains?(*selector_or_xpath_args) ⇒ Boolean

Returns true if this element has a child matching the provided selector

Parameters:

  • selector_or_xpath_args (Array<String>)

    CSS selectors or XPath arguments

Returns:

  • (Boolean)


643
644
645
# File 'lib/kitchen/element_base.rb', line 643

def contains?(*selector_or_xpath_args)
  !node.at(*selector_or_xpath_args).nil?
end

#content(*selector_or_xpath_args) ⇒ String

Get the content of children matching the provided selector. Mostly useful when there is one child with text you want to extract.

Parameters:

  • selector_or_xpath_args (Array<String>)

    CSS selectors or XPath arguments

Returns:



634
635
636
# File 'lib/kitchen/element_base.rb', line 634

def content(*selector_or_xpath_args)
  node.search(*selector_or_xpath_args).children.to_s
end

#copied_idObject

Copy the element’s id



509
510
511
512
# File 'lib/kitchen/element_base.rb', line 509

def copied_id
  id_tracker.record_id_copied(id)
  id_tracker.modified_id_to_paste(id)
end

#copy(to: nil) ⇒ Element

Makes a copy of the element and places it on the specified clipboard.

Parameters:

  • to (Symbol, String, Clipboard, nil) (defaults to: nil)

    the name of the clipboard (or a Clipboard object) to cut to. String values are converted to symbols. If not provided, the copy is not placed on a clipboard.

Returns:



477
478
479
480
481
482
483
484
485
486
487
488
489
# File 'lib/kitchen/element_base.rb', line 477

def copy(to: nil)
  # See `clone` method for a note about namespaces
  block_error_if(block_given?)

  the_copy = clone
  the_copy.raw.traverse do |node|
    next if node.text? || node.document?

    id_tracker.record_id_copied(node[:id])
  end
  get_clipboard(to).add(the_copy) if to.present?
  the_copy
end

#count_in(ancestor_type) ⇒ Object

Returns the count of this element’s type in the given ancestor type

Parameters:

  • ancestor_type (String, Symbol)


333
334
335
336
# File 'lib/kitchen/element_base.rb', line 333

def count_in(ancestor_type)
  @ancestors[ancestor_type]&.get_descendant_count(short_type) ||
    raise("No ancestor of type '#{ancestor_type}'")
end

#cut(to: nil) ⇒ Element

Removes the element from its parent and places it on the specified clipboard

Parameters:

  • to (Symbol, String, Clipboard, nil) (defaults to: nil)

    the name of the clipboard (or a Clipboard object) to cut to. String values are converted to symbols. If not provided, the element is not placed on a clipboard.

Returns:



457
458
459
460
461
462
463
464
465
466
467
468
# File 'lib/kitchen/element_base.rb', line 457

def cut(to: nil)
  block_error_if(block_given?)

  raw.traverse do |node|
    next if node.text? || node.document?

    id_tracker.record_id_cut(node[:id])
  end
  node.remove
  get_clipboard(to).add(self) if to.present?
  self
end

#data_typeString

Returns the element’s data-type

Returns:



222
223
224
# File 'lib/kitchen/element_base.rb', line 222

def data_type
  self[:'data-type']
end

#element_childrenTypeCastingElementEnumerator

Returns an enumerator over the direct child elements of this element, with the specific type (e.g. TermElement) if such type is available.



442
443
444
445
446
447
448
# File 'lib/kitchen/element_base.rb', line 442

def element_children
  block_error_if(block_given?)
  TypeCastingElementEnumerator.factory.build_within(
    self,
    search_query: SearchQuery.new(css_or_xpath: './*')
  )
end

#first(*selector_or_xpath_args, reload: false) {|the| ... } ⇒ Element? Also known as: at

Yields and returns the first child element that matches the provided selector or XPath arguments.

Parameters:

  • selector_or_xpath_args (Array<String>)

    CSS selectors or XPath arguments

  • reload (Boolean) (defaults to: false)

    ignores cache if true

Yield Parameters:

  • the (Element)

    matched XML element

Returns:

  • (Element, nil)

    the matched XML element or nil if no match found



412
413
414
415
416
# File 'lib/kitchen/element_base.rb', line 412

def first(*selector_or_xpath_args, reload: false)
  search(*selector_or_xpath_args, reload: reload).first.tap do |element|
    yield(element) if block_given?
  end
end

#first!(*selector_or_xpath_args, reload: false) {|the| ... } ⇒ Element

Yields and returns the first child element that matches the provided selector or XPath arguments.

Parameters:

  • selector_or_xpath_args (Array<String>)

    CSS selectors or XPath arguments

  • reload (Boolean) (defaults to: false)

    ignores cache if true

Yield Parameters:

  • the (Element)

    matched XML element

Returns:

  • (Element)

    the matched XML element

Raises:



427
428
429
430
431
# File 'lib/kitchen/element_base.rb', line 427

def first!(*selector_or_xpath_args, reload: false)
  search(*selector_or_xpath_args, reload: reload).first!.tap do |element|
    yield(element) if block_given?
  end
end

#has_ancestor?(type) ⇒ Boolean

Returns true iff this element has an ancestor of the given type

Parameters:

  • type (String, Symbol)

    e.g. :page, :term

Returns:

  • (Boolean)


275
276
277
# File 'lib/kitchen/element_base.rb', line 275

def has_ancestor?(type)
  @ancestors[type.to_sym].present?
end

#has_class?(klass) ⇒ Boolean

Returns true if this element has the given class

Parameters:

  • klass (String)

    the class to test for

Returns:

  • (Boolean)


198
199
200
# File 'lib/kitchen/element_base.rb', line 198

def has_class?(klass)
  (self[:class] || '').include?(klass)
end

#hrefString

Returns the element’s href

Returns:



230
231
232
# File 'lib/kitchen/element_base.rb', line 230

def href
  self[:href]
end

#href=(value) ⇒ Object

Sets the element’s href

Parameters:

  • value (String)

    the new value for the href



238
239
240
# File 'lib/kitchen/element_base.rb', line 238

def href=(value)
  self[:href] = value
end

#idString

Returns the element’s ID

Returns:



206
207
208
# File 'lib/kitchen/element_base.rb', line 206

def id
  self[:id]
end

#id=(value) ⇒ Object

Sets the element’s ID

Parameters:

  • value (String)

    the new value for the ID



214
215
216
# File 'lib/kitchen/element_base.rb', line 214

def id=(value)
  self[:id] = value
end

#inner_html=Object

Set the inner HTML for this element

Returns:

  • Object

See Also:



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#inspectString

Returns a string version of this element

Returns:



683
684
685
# File 'lib/kitchen/element_base.rb', line 683

def inspect
  to_s
end

#is?(type) ⇒ Boolean

Returns true if this element is the given type

Parameters:

  • type (Symbol)

    the descendant type, e.g. ‘:page`

Returns:

  • (Boolean)

Raises:

  • if the type is unknown



179
180
181
# File 'lib/kitchen/element_base.rb', line 179

def is?(type)
  ElementBase.descendant!(type).is_the_element_class_for?(raw, config: config)
end

#key?(attribute) ⇒ Object

Returns true if attribute is set



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#mark_as_current_location!Object

Mark the location so that if there’s an error we can show the developer where.



667
668
669
# File 'lib/kitchen/element_base.rb', line 667

def mark_as_current_location!
  document.location = self
end

#nameString

Get the element name (the tag)

Returns:

See Also:



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#name=Object

Set the element name (the tag)



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#pagesObject

Returns a pages enumerator



774
775
776
777
# File 'lib/kitchen/element_base.rb', line 774

def_delegators :as_enumerator, :pages, :chapters, :terms, :figures, :notes, :tables, :examples,
:metadatas, :non_introduction_pages, :units, :titles, :exercises, :references,
:composite_pages, :composite_chapters, :solutions, :injected_questions,
:search_with, :sections, :injected_exercises

#pantryPantry

Access the pantry for this element’s document

Returns:



109
# File 'lib/kitchen/element_base.rb', line 109

def_delegators :document, :pantry, :clipboard

#parentObject



521
522
523
# File 'lib/kitchen/element_base.rb', line 521

def parent
  Element.new(node: raw.parent, document: document, short_type: "parent(#{short_type})")
end

#pasteObject

When an element is cut or copied, use this method to get the element’s content; keeps IDs unique



493
494
495
496
497
498
499
500
501
502
503
504
505
506
# File 'lib/kitchen/element_base.rb', line 493

def paste
  # See `clone` method for a note about namespaces
  block_error_if(block_given?)
  temp_copy = clone
  temp_copy.raw.traverse do |node|
    next if node.text? || node.document?

    if node[:id].present?
      id_tracker.record_id_pasted(node[:id])
      node[:id] = id_tracker.modified_id_to_paste(node[:id])
    end
  end
  temp_copy.to_s
end

#pathString

Get the path for this element

Returns:

See Also:



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#preceded_by_textObject

Returns true if the immediately preceding sibling is text

Returns:

  • Boolean



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#prepend(child: nil, sibling: nil) ⇒ Object

If child argument given, prepends it before the element’s current children. If sibling is given, prepends it as a sibling to this element.

Parameters:

  • child (String) (defaults to: nil)

    the child to prepend

  • sibling (String) (defaults to: nil)

    the sibling to prepend

Raises:

  • (RecipeError)

    if specify other than just a child or a sibling



549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
# File 'lib/kitchen/element_base.rb', line 549

def prepend(child: nil, sibling: nil)
  require_one_of_child_or_sibling(child, sibling)

  if child
    if node.children.empty?
      node.children = child.to_s
    else
      node.children.first.add_previous_sibling(child)
    end
  else
    node.add_previous_sibling(sibling)
  end

  self
end

#previousObject

returns previous element sibling (only returns elements or nil) nil if there’s no previous sibling



529
530
531
532
533
534
535
536
537
538
# File 'lib/kitchen/element_base.rb', line 529

def previous
  prev = raw.previous_element
  return prev if prev.nil?

  Element.new(
    node: prev,
    document: document,
    short_type: "previous(#{short_type})"
  )
end

#rawNokogiri::XML::Node

Returns the underlying Nokogiri object

Returns:



675
676
677
# File 'lib/kitchen/element_base.rb', line 675

def raw
  node
end

#raw_search(*selector_or_xpath_args, reload: false) ⇒ Object



396
397
398
399
400
401
402
# File 'lib/kitchen/element_base.rb', line 396

def raw_search(*selector_or_xpath_args, reload: false)
  key = selector_or_xpath_args
  @search_cache[key] = nil if reload || !config.enable_search_cache
  # cache nil search results with a fake -1 value
  @search_cache[key] ||= raw.search(*selector_or_xpath_args) || -1
  @search_cache[key] == -1 ? nil : @search_cache[key]
end

#remember_that_a_sub_element_was_counted(search_query, type) ⇒ Object

Track that a sub element found by the given query has been counted

Parameters:

  • search_query (SearchQuery)

    the search query matching the counted element

  • type (String)

    the type of the sub element that was counted



343
344
345
346
# File 'lib/kitchen/element_base.rb', line 343

def remember_that_a_sub_element_was_counted(search_query, type)
  @search_query_matches_that_have_been_counted[search_query.to_s] ||= Hash.new(0)
  @search_query_matches_that_have_been_counted[search_query.to_s][type] += 1
end

#remove_attributeObject

Removes an attribute from the element



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#remove_classObject

Remove a class from the element



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#replace_children(with:) ⇒ Object

Replaces this element’s children

Parameters:

  • with (String)

    the children to substitute for the current children



592
593
594
595
# File 'lib/kitchen/element_base.rb', line 592

def replace_children(with:)
  node.children = with
  self
end

#search(*selector_or_xpath_args, only: nil, except: nil, reload: false) ⇒ ElementEnumerator

Returns an ElementEnumerator that iterates over the provided selector or xpath queries

Parameters:

  • selector_or_xpath_args (Array<String>)

    Selector or XPath queries

  • only (Symbol, Callable) (defaults to: nil)

    the name of a method to call on an element or a lambda or proc that accepts an element; elements will only be included in the search results if the method or callable returns true

  • except (Symbol, Callable) (defaults to: nil)

    the name of a method to call on an element or a lambda or proc that accepts an element; elements will not be included in the search results if the method or callable returns false

Returns:



382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/kitchen/element_base.rb', line 382

def search(*selector_or_xpath_args, only: nil, except: nil, reload: false)
  block_error_if(block_given?)

  ElementEnumerator.factory.build_within(
    self,
    search_query: SearchQuery.new(
      css_or_xpath: selector_or_xpath_args,
      only: only,
      except: except
    ),
    reload: reload
  )
end

#search_historySearchHistory

Returns the search history that found this element

Returns:



364
365
366
367
368
369
# File 'lib/kitchen/element_base.rb', line 364

def search_history
  SearchHistory.new(
    ancestor_elements.last&.search_history || SearchHistory.empty,
    search_query_that_found_me
  )
end

#selectorsSelectors::Base

Get the selectors for this element’s document

Returns:



101
# File 'lib/kitchen/element_base.rb', line 101

def_delegators :config, :selectors

#set(property, value) ⇒ Object

A way to set values and chain them

Examples:

element.set(:name,"div").set("id","foo")

Parameters:

  • property (String, Symbol)

    the name of the property to set

  • value (String)

    the value to set



250
251
252
253
254
255
256
257
258
# File 'lib/kitchen/element_base.rb', line 250

def set(property, value)
  case property.to_sym
  when :name
    self.name = value
  else
    self[property.to_sym] = value
  end
  self
end

#sub_header_nameString

Returns the header tag name that is one level under the first header tag in this element, e.g. if this element is a “div” whose first header is “h1”, this will return “h2”

TODO this method may not be needed.

Returns:

  • (String)

    the sub header tag name



655
656
657
658
659
660
661
662
663
# File 'lib/kitchen/element_base.rb', line 655

def sub_header_name
  first_header = node.search('h1, h2, h3, h4, h5, h6').first

  if first_header.nil?
    'h1'
  else
    first_header.name.gsub(/\d/) { |num| (num.to_i + 1).to_s }
  end
end

#target_label(label_text: nil, custom_content: nil, cases: false) ⇒ Pantry

Creates labels for links to inside elements like Figures, Tables, Equations, Exercises, Notes.

Parameters:

  • label_text (String) (defaults to: nil)

    label of the element defined in yml file. (e.g. “Figure”, “Table”, “Equation”)

  • custom_content (String) (defaults to: nil)

    might be numbering of the element or text copied from content (e.g. note title)

  • cases (Boolean) (defaults to: false)

    true if labels should use grammatical cases (used in Polish books)

Returns:



750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
# File 'lib/kitchen/element_base.rb', line 750

def target_label(label_text: nil, custom_content: nil, cases: false)
  if cases
    cases = %w[nominative genitive dative accusative instrumental locative vocative]
    element_labels = {}

    cases.each do |label_case|
      element_labels[label_case] = "#{I18n.t("#{label_text}.#{label_case}")} #{custom_content}"

      element_label_case = element_labels[label_case]

      pantry(name: "#{label_case}_link_text").store element_label_case, label: id if id
    end
  else
    element_label = if label_text
                      "#{I18n.t(label_text.to_s)} #{custom_content}"
                    else
                      custom_content
                    end
    pantry(name: :link_text).store element_label, label: id if id
  end
end

#textString

Get the element text

Returns:

See Also:



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#to_htmlString

Get the element as HTML

Returns:

See Also:



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#to_sString

Returns a string version of this element

Returns:



691
692
693
# File 'lib/kitchen/element_base.rb', line 691

def to_s
  remove_default_namespaces_if_clone(node.to_s)
end

#to_xhtmlString

Returns a string version of this element as XHTML

Returns:



707
708
709
# File 'lib/kitchen/element_base.rb', line 707

def to_xhtml
  remove_default_namespaces_if_clone(node.to_xhtml)
end

#to_xmlString

Returns a string version of this element as XML

Returns:



699
700
701
# File 'lib/kitchen/element_base.rb', line 699

def to_xml
  remove_default_namespaces_if_clone(node.to_xml)
end

#trashObject

Delete the element



516
517
518
519
# File 'lib/kitchen/element_base.rb', line 516

def trash
  node.remove
  self
end

#uncount(search_query) ⇒ Object

Undo the counts from a prior search query (so that they can be counted again)

Parameters:

  • search_query (SearchQuery)

    the prior search query whose counts need to be undone



352
353
354
355
356
357
358
# File 'lib/kitchen/element_base.rb', line 352

def uncount(search_query)
  @search_query_matches_that_have_been_counted.delete(search_query.to_s)&.each do |type, count|
    ancestors.each_value do |ancestor|
      ancestor.decrement_descendant_count(type, by: count)
    end
  end
end

#wrapNokogiri::XML::Node

Add HTML around this element



88
89
90
91
# File 'lib/kitchen/element_base.rb', line 88

def_delegators :@node, :name=, :name, :[], :[]=, :add_class, :remove_class,
:text, :wrap, :children, :to_html, :remove_attribute,
:key?, :classes, :path, :inner_html=, :add_previous_sibling,
:preceded_by_text?

#wrap_children(name = 'div', attributes = {}) {|the| ... } ⇒ Element

Wraps the element’s children in a new element. Yields the new wrapper element to a block, if provided.

Parameters:

  • name (String) (defaults to: 'div')

    the wrapper’s tag name, defaults to ‘div’.

  • attributes (Hash) (defaults to: {})

    the wrapper’s attributes. XML attributes often use hyphens (e.g. ‘data-type’) which are hard to put into symbols. Therefore underscores in keys passed to this method will be converted to hyphens. If you really want an underscore you can use a double underscore.

Yield Parameters:

  • the (Element)

    wrapper Element

Returns:



608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
# File 'lib/kitchen/element_base.rb', line 608

def wrap_children(name='div', attributes={})
  if name.is_a?(Hash)
    attributes = name
    name = 'div'
  end

  node.children = node.document.create_element(name) do |new_node|
    # For some reason passing attributes to create_element doesn't work, so doing here
    attributes.each do |k, v|
      new_node[k.to_s.gsub(/([^_])_([^_])/, '\1-\2').gsub('__', '_')] = v
    end
    new_node.children = children
    yield Element.new(node: new_node, document: document, short_type: nil) if block_given?
  end

  self
end