openstax_kitchen 4.1.0 → 4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/README.md +16 -0
- data/codecov.yaml +1 -0
- data/lib/kitchen/book_document.rb +1 -1
- data/lib/kitchen/chapter_element.rb +2 -2
- data/lib/kitchen/composite_chapter_element_enumerator.rb +21 -0
- data/lib/kitchen/config.rb +7 -0
- data/lib/kitchen/directions/bake_chapter_key_concepts/v1.rb +1 -1
- data/lib/kitchen/directions/bake_composite_chapters.rb +1 -1
- data/lib/kitchen/directions/bake_composite_pages.rb +1 -1
- data/lib/kitchen/directions/bake_equations.rb +1 -1
- data/lib/kitchen/directions/book_answer_key_container/v1.rb +1 -1
- data/lib/kitchen/directions/chapter_review_container/v1.rb +1 -1
- data/lib/kitchen/directions/move_solutions_to_answer_key/strategies/uphysics.rb +2 -2
- data/lib/kitchen/document.rb +2 -10
- data/lib/kitchen/element_base.rb +19 -7
- data/lib/kitchen/element_enumerator_base.rb +24 -1
- data/lib/kitchen/element_enumerator_factory.rb +19 -7
- data/lib/kitchen/exercise_element.rb +2 -2
- data/lib/kitchen/oven.rb +2 -0
- data/lib/kitchen/page_element.rb +5 -2
- data/lib/kitchen/patches/nokogiri.rb +15 -0
- data/lib/kitchen/patches/nokogiri_profiling.rb +60 -0
- data/lib/kitchen/search_query.rb +6 -0
- data/lib/kitchen/selector.rb +2 -1
- data/lib/kitchen/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b80bf07271b311949246bf6a63a9d10fe5f14396175f66a129debcc9e9ef15e3
|
4
|
+
data.tar.gz: 7a3a7050d134a0b106d3a80dc18add9209742fca1eea4873f351eb0dd4d05034
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7fee3b2b4dd92639af2e20520c3f65a31255d944df649e91c5c0e5c8f0a8e1e1f6d19b4477480c5cbc899f7124727c8a40b5a0759985ec94225885baf36b3287
|
7
|
+
data.tar.gz: ed62932aa44e597135158cc182daefd2773b89070e1d82750669d4d1f760fa8179f1197c67eddcdec73c1cdc2b1bd7537ca28c8f5603d3a2a7962457d777141f
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [4.1.1] - 2021-05-24
|
10
|
+
|
11
|
+
* Adds low level Nokogiri caching, disabled by default (patch)
|
12
|
+
* Cache Selector objects since they don't change (patch)
|
13
|
+
* Use more specific selectors when to reduce bake time (patch)
|
14
|
+
|
9
15
|
## [4.1.0] - 2021-05-18
|
10
16
|
|
11
17
|
* Fixed performance problem with element class detection (patch)
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -641,6 +641,22 @@ expect(book_1).to match_normalized_html("some string of HTML here")
|
|
641
641
|
expect(book_1).to match_html_nodes("some string of HTML here")
|
642
642
|
```
|
643
643
|
|
644
|
+
### Profiling
|
645
|
+
|
646
|
+
If you set the `PROFILE` environment variable to something before you run specs or a recipe, search query profile data will be collected and printed, e.g.
|
647
|
+
|
648
|
+
```bash
|
649
|
+
%> PROFILE=1 rspec
|
650
|
+
```
|
651
|
+
|
652
|
+
### Caching
|
653
|
+
|
654
|
+
There's a low-level CSS query caching tool that saves repeated queries. In some tests, it saves 15% of query time. It is disabled by default (because we aren't super sure that it is completely safe) but can be turned on with
|
655
|
+
|
656
|
+
```ruby
|
657
|
+
doc.config.enable_search_cache = true
|
658
|
+
```
|
659
|
+
|
644
660
|
### VSCode
|
645
661
|
|
646
662
|
1. Visit `vscode:extension/ms-vscode-remote.remote-containers` in a browser
|
data/codecov.yaml
CHANGED
@@ -41,7 +41,7 @@ module Kitchen
|
|
41
41
|
# @return [Element, nil]
|
42
42
|
#
|
43
43
|
def introduction_page
|
44
|
-
pages('
|
44
|
+
pages('$.introduction').first
|
45
45
|
end
|
46
46
|
|
47
47
|
# Returns an enumerator for the glossaries
|
@@ -65,7 +65,7 @@ module Kitchen
|
|
65
65
|
# @return [ElementEnumerator]
|
66
66
|
#
|
67
67
|
def abstracts
|
68
|
-
search('[data-type="abstract"]')
|
68
|
+
search('div[data-type="abstract"]')
|
69
69
|
end
|
70
70
|
|
71
71
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kitchen
|
4
|
+
# An enumerator for composite page elements
|
5
|
+
#
|
6
|
+
class CompositeChapterElementEnumerator < ElementEnumeratorBase
|
7
|
+
|
8
|
+
# Returns a factory for this enumerator
|
9
|
+
#
|
10
|
+
# @return [ElementEnumeratorFactory]
|
11
|
+
#
|
12
|
+
def self.factory
|
13
|
+
ElementEnumeratorFactory.new(
|
14
|
+
default_css_or_xpath: Selector.named(:composite_chapter),
|
15
|
+
sub_element_class: CompositeChapterElement,
|
16
|
+
enumerator_class: self
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/kitchen/config.rb
CHANGED
@@ -17,6 +17,12 @@ module Kitchen
|
|
17
17
|
#
|
18
18
|
attr_accessor :enable_all_namespaces
|
19
19
|
|
20
|
+
# @!attribute [rw] enable_search_cache
|
21
|
+
#
|
22
|
+
# @return [Boolean]
|
23
|
+
#
|
24
|
+
attr_accessor :enable_search_cache
|
25
|
+
|
20
26
|
# Creates a new config from a file (not implemented)
|
21
27
|
#
|
22
28
|
def self.new_from_file(_file)
|
@@ -28,6 +34,7 @@ module Kitchen
|
|
28
34
|
def initialize(hash: {}, selectors: nil)
|
29
35
|
@selectors = selectors || Kitchen::Selectors::Standard1.new
|
30
36
|
@enable_all_namespaces = hash[:enable_all_namespaces] || true
|
37
|
+
@enable_search_cache = hash[:enable_search_cache] || false
|
31
38
|
@hash = hash
|
32
39
|
end
|
33
40
|
end
|
@@ -19,7 +19,7 @@ module Kitchen::Directions::BakeChapterKeyConcepts
|
|
19
19
|
key_concepts.each do |key_concept|
|
20
20
|
key_concept.prepend(child: title)
|
21
21
|
key_concept.wrap("<div class='os-section-area'>")
|
22
|
-
page.search('.os-section-area').first.cut(to: key_concepts_clipboard)
|
22
|
+
page.search('div.os-section-area').first.cut(to: key_concepts_clipboard)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -4,7 +4,7 @@ module Kitchen
|
|
4
4
|
module Directions
|
5
5
|
module BakeCompositeChapters
|
6
6
|
def self.v1(book:)
|
7
|
-
book.
|
7
|
+
book.composite_chapters.each do |chapter|
|
8
8
|
chapter.first("[data-type='document-title']").id =
|
9
9
|
"composite-chapter-#{chapter.count_in(:book)}"
|
10
10
|
end
|
@@ -4,7 +4,7 @@ module Kitchen
|
|
4
4
|
module Directions
|
5
5
|
module BakeEquations
|
6
6
|
def self.v1(book:, number_decorator: :none)
|
7
|
-
book.chapters.search('[data-type="equation"]:not(.unnumbered)').each do |eq|
|
7
|
+
book.chapters.search('div[data-type="equation"]:not(.unnumbered)').each do |eq|
|
8
8
|
chapter = eq.ancestor(:chapter)
|
9
9
|
number = "#{chapter.count_in(:book)}.#{eq.count_in(:chapter)}"
|
10
10
|
|
@@ -7,7 +7,7 @@ module Kitchen::Directions::BookAnswerKeyContainer
|
|
7
7
|
def bake(book:)
|
8
8
|
@metadata = book.metadata.children_to_keep.copy
|
9
9
|
book.body.append(child: render(file: 'eob_solutions_container.xhtml.erb'))
|
10
|
-
book.body.first('.os-eob.os-solutions-container')
|
10
|
+
book.body.first('div.os-eob.os-solutions-container')
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -7,7 +7,7 @@ module Kitchen::Directions::ChapterReviewContainer
|
|
7
7
|
def bake(chapter:, metadata_source:)
|
8
8
|
@metadata = metadata_source.children_to_keep.copy
|
9
9
|
chapter.append(child: render(file: 'chapter_review.xhtml.erb'))
|
10
|
-
chapter.first('.os-eoc.os-chapter-review-container')
|
10
|
+
chapter.first('div.os-eoc.os-chapter-review-container')
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -18,7 +18,7 @@ module Kitchen::Directions::MoveSolutionsToAnswerKey
|
|
18
18
|
def bake_section(chapter:, append_to:, klass:)
|
19
19
|
section_solutions_set = []
|
20
20
|
chapter.search(".#{klass}").each do |section|
|
21
|
-
section.search('[data-type="solution"]').each do |solution|
|
21
|
+
section.search('div[data-type="solution"]').each do |solution|
|
22
22
|
section_solutions_set.push(solution.cut)
|
23
23
|
end
|
24
24
|
end
|
@@ -31,7 +31,7 @@ module Kitchen::Directions::MoveSolutionsToAnswerKey
|
|
31
31
|
|
32
32
|
def bake_from_notes(chapter:, append_to:, klass:)
|
33
33
|
solutions = []
|
34
|
-
chapter.notes("
|
34
|
+
chapter.notes("$.#{klass}").each do |note|
|
35
35
|
note.exercises.each do |exercise|
|
36
36
|
solution = exercise.solution
|
37
37
|
solutions.push(solution.cut) if solution
|
data/lib/kitchen/document.rb
CHANGED
@@ -47,16 +47,8 @@ module Kitchen
|
|
47
47
|
@next_paste_count_for_id = {}
|
48
48
|
@id_copy_suffix = '_copy_'
|
49
49
|
|
50
|
-
# Nokogiri by default only recognizes the namespaces on the root node.
|
51
|
-
|
52
|
-
return unless @config.enable_all_namespaces && raw.present?
|
53
|
-
|
54
|
-
raw.collect_namespaces.each do |namespace, url|
|
55
|
-
prefix, name = namespace.split(':')
|
56
|
-
next unless prefix == 'xmlns' && name.present?
|
57
|
-
|
58
|
-
raw.root.add_namespace_definition(name, url)
|
59
|
-
end
|
50
|
+
# Nokogiri by default only recognizes the namespaces on the root node. Add all others.
|
51
|
+
raw&.add_all_namespaces! if @config.enable_all_namespaces
|
60
52
|
end
|
61
53
|
|
62
54
|
# Returns an enumerator that iterates over all children of this document
|
data/lib/kitchen/element_base.rb
CHANGED
@@ -132,6 +132,7 @@ module Kitchen
|
|
132
132
|
@ancestors = HashWithIndifferentAccess.new
|
133
133
|
@search_query_matches_that_have_been_counted = {}
|
134
134
|
@is_a_clone = false
|
135
|
+
@search_cache = {}
|
135
136
|
end
|
136
137
|
|
137
138
|
# Returns ElementBase descendent type or nil if none found
|
@@ -345,7 +346,7 @@ module Kitchen
|
|
345
346
|
# search results if the method or callable returns false
|
346
347
|
# @return [ElementEnumerator]
|
347
348
|
#
|
348
|
-
def search(*selector_or_xpath_args, only: nil, except: nil)
|
349
|
+
def search(*selector_or_xpath_args, only: nil, except: nil, reload: false)
|
349
350
|
block_error_if(block_given?)
|
350
351
|
|
351
352
|
ElementEnumerator.factory.build_within(
|
@@ -354,19 +355,29 @@ module Kitchen
|
|
354
355
|
css_or_xpath: selector_or_xpath_args,
|
355
356
|
only: only,
|
356
357
|
except: except
|
357
|
-
)
|
358
|
+
),
|
359
|
+
reload: reload
|
358
360
|
)
|
359
361
|
end
|
360
362
|
|
363
|
+
def raw_search(*selector_or_xpath_args, reload: false)
|
364
|
+
key = selector_or_xpath_args
|
365
|
+
@search_cache[key] = nil if reload || !config.enable_search_cache
|
366
|
+
# cache nil search results with a fake -1 value
|
367
|
+
@search_cache[key] ||= raw.search(*selector_or_xpath_args) || -1
|
368
|
+
@search_cache[key] == -1 ? nil : @search_cache[key]
|
369
|
+
end
|
370
|
+
|
361
371
|
# Yields and returns the first child element that matches the provided
|
362
372
|
# selector or XPath arguments.
|
363
373
|
#
|
364
374
|
# @param selector_or_xpath_args [Array<String>] CSS selectors or XPath arguments
|
375
|
+
# @param reload [Boolean] ignores cache if true
|
365
376
|
# @yieldparam [Element] the matched XML element
|
366
377
|
# @return [Element, nil] the matched XML element or nil if no match found
|
367
378
|
#
|
368
|
-
def first(*selector_or_xpath_args)
|
369
|
-
search(*selector_or_xpath_args).first.tap do |element|
|
379
|
+
def first(*selector_or_xpath_args, reload: false)
|
380
|
+
search(*selector_or_xpath_args, reload: reload).first.tap do |element|
|
370
381
|
yield(element) if block_given?
|
371
382
|
end
|
372
383
|
end
|
@@ -375,12 +386,13 @@ module Kitchen
|
|
375
386
|
# selector or XPath arguments.
|
376
387
|
#
|
377
388
|
# @param selector_or_xpath_args [Array<String>] CSS selectors or XPath arguments
|
389
|
+
# @param reload [Boolean] ignores cache if true
|
378
390
|
# @yieldparam [Element] the matched XML element
|
379
391
|
# @raise [ElementNotFoundError] if no matching element is found
|
380
392
|
# @return [Element] the matched XML element
|
381
393
|
#
|
382
|
-
def first!(*selector_or_xpath_args)
|
383
|
-
search(*selector_or_xpath_args).first!.tap do |element|
|
394
|
+
def first!(*selector_or_xpath_args, reload: false)
|
395
|
+
search(*selector_or_xpath_args, reload: reload).first!.tap do |element|
|
384
396
|
yield(element) if block_given?
|
385
397
|
end
|
386
398
|
end
|
@@ -694,7 +706,7 @@ module Kitchen
|
|
694
706
|
# Returns a pages enumerator
|
695
707
|
def_delegators :as_enumerator, :pages, :chapters, :terms, :figures, :notes, :tables, :examples,
|
696
708
|
:metadatas, :non_introduction_pages, :units, :titles, :exercises, :references,
|
697
|
-
:composite_pages
|
709
|
+
:composite_pages, :composite_chapters
|
698
710
|
|
699
711
|
# Returns this element as an enumerator (over only one element, itself)
|
700
712
|
#
|
@@ -84,7 +84,30 @@ module Kitchen
|
|
84
84
|
#
|
85
85
|
def composite_pages(css_or_xpath=nil, only: nil, except: nil)
|
86
86
|
block_error_if(block_given?)
|
87
|
-
chain_to(CompositePageElementEnumerator,
|
87
|
+
chain_to(CompositePageElementEnumerator,
|
88
|
+
css_or_xpath: css_or_xpath,
|
89
|
+
only: only,
|
90
|
+
except: except)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns an enumerator that iterates through composite chapters within the scope of this enumerator
|
94
|
+
#
|
95
|
+
# @param css_or_xpath [String] additional selectors to further narrow the element iterated over;
|
96
|
+
# a "$" in this argument will be replaced with the default selector for the element being
|
97
|
+
# iterated over.
|
98
|
+
# @param only [Symbol, Callable] the name of a method to call on an element or a
|
99
|
+
# lambda or proc that accepts an element; elements will only be included in the
|
100
|
+
# search results if the method or callable returns true
|
101
|
+
# @param except [Symbol, Callable] the name of a method to call on an element or a
|
102
|
+
# lambda or proc that accepts an element; elements will not be included in the
|
103
|
+
# search results if the method or callable returns false
|
104
|
+
#
|
105
|
+
def composite_chapters(css_or_xpath=nil, only: nil, except: nil)
|
106
|
+
block_error_if(block_given?)
|
107
|
+
chain_to(CompositeChapterElementEnumerator,
|
108
|
+
css_or_xpath: css_or_xpath,
|
109
|
+
only: only,
|
110
|
+
except: except)
|
88
111
|
end
|
89
112
|
|
90
113
|
# Returns an enumerator that iterates through pages that arent the introduction page within the scope of this enumerator
|
@@ -39,12 +39,22 @@ module Kitchen
|
|
39
39
|
# @return [ElementEnumeratorBase] actually returns the concrete enumerator class
|
40
40
|
# given to the factory in its constructor.
|
41
41
|
#
|
42
|
-
def build_within(enumerator_or_element, search_query: SearchQuery.new)
|
42
|
+
def build_within(enumerator_or_element, search_query: SearchQuery.new, reload: false)
|
43
43
|
case enumerator_or_element
|
44
44
|
when ElementBase
|
45
|
-
build_within_element(enumerator_or_element,
|
45
|
+
build_within_element(enumerator_or_element,
|
46
|
+
search_query: search_query,
|
47
|
+
reload: reload)
|
46
48
|
when ElementEnumeratorBase
|
47
|
-
|
49
|
+
if enumerator_class != ElementEnumerator && !search_query.expects_substitution?
|
50
|
+
raise "Query #{search_query} is missing the substitution character ('$') but " \
|
51
|
+
"is run with an enumerator #{enumerator_class.name} that has its own " \
|
52
|
+
"selectors for substitution."
|
53
|
+
end
|
54
|
+
|
55
|
+
build_within_other_enumerator(enumerator_or_element,
|
56
|
+
search_query: search_query,
|
57
|
+
reload: reload)
|
48
58
|
end
|
49
59
|
end
|
50
60
|
|
@@ -64,7 +74,7 @@ module Kitchen
|
|
64
74
|
|
65
75
|
protected
|
66
76
|
|
67
|
-
def build_within_element(element, search_query:)
|
77
|
+
def build_within_element(element, search_query:, reload:)
|
68
78
|
search_query.apply_default_css_or_xpath_and_normalize(default_css_or_xpath,
|
69
79
|
config: element.config)
|
70
80
|
|
@@ -77,7 +87,7 @@ module Kitchen
|
|
77
87
|
# below, the counts are correct.
|
78
88
|
element.uncount(search_query)
|
79
89
|
|
80
|
-
element.
|
90
|
+
element.raw_search(*search_query.css_or_xpath, reload: reload).each do |sub_node|
|
81
91
|
sub_element = ElementFactory.build_from_node(
|
82
92
|
node: sub_node,
|
83
93
|
document: element.document,
|
@@ -105,13 +115,15 @@ module Kitchen
|
|
105
115
|
end
|
106
116
|
end
|
107
117
|
|
108
|
-
def build_within_other_enumerator(other_enumerator, search_query:)
|
118
|
+
def build_within_other_enumerator(other_enumerator, search_query:, reload:)
|
109
119
|
# Return a new enumerator instance that internally iterates over `other_enumerator`
|
110
120
|
# running a new enumerator for each element returned by that other enumerator.
|
111
121
|
enumerator_class.new(search_query: search_query,
|
112
122
|
upstream_enumerator: other_enumerator) do |block|
|
113
123
|
other_enumerator.each do |element|
|
114
|
-
build_within_element(element,
|
124
|
+
build_within_element(element,
|
125
|
+
search_query: search_query,
|
126
|
+
reload: reload).each do |sub_element|
|
115
127
|
block.yield(sub_element)
|
116
128
|
end
|
117
129
|
end
|
@@ -28,7 +28,7 @@ module Kitchen
|
|
28
28
|
# @return ElementEnumerator
|
29
29
|
#
|
30
30
|
def problem
|
31
|
-
first("[data-type='problem']")
|
31
|
+
first("div[data-type='problem']")
|
32
32
|
end
|
33
33
|
|
34
34
|
# Returns the enumerator for solution.
|
@@ -36,7 +36,7 @@ module Kitchen
|
|
36
36
|
# @return ElementEnumerator
|
37
37
|
#
|
38
38
|
def solution
|
39
|
-
first("[data-type='solution']")
|
39
|
+
first("div[data-type='solution']")
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
data/lib/kitchen/oven.rb
CHANGED
data/lib/kitchen/page_element.rb
CHANGED
@@ -29,9 +29,12 @@ module Kitchen
|
|
29
29
|
# @raise [ElementNotFoundError] if no matching element is found
|
30
30
|
# @return [Element]
|
31
31
|
#
|
32
|
-
def title
|
32
|
+
def title(reload: false)
|
33
33
|
# The selector for intro titles changes during the baking process
|
34
|
-
|
34
|
+
@title ||= begin
|
35
|
+
selector = is_introduction? ? selectors.title_in_introduction_page : selectors.title_in_page
|
36
|
+
first!(selector, reload: reload)
|
37
|
+
end
|
35
38
|
end
|
36
39
|
|
37
40
|
# Returns the title's text regardless of whether the title has been baked
|
@@ -31,6 +31,21 @@ module Nokogiri
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
34
|
+
|
35
|
+
def add_all_namespaces!
|
36
|
+
# Nokogiri by default only recognizes the namespaces on the root node. Collect all
|
37
|
+
# namespaces and add them manually.
|
38
|
+
return if @all_namespaces_added
|
39
|
+
|
40
|
+
collect_namespaces.each do |namespace, url|
|
41
|
+
prefix, name = namespace.split(':')
|
42
|
+
next unless prefix == 'xmlns' && name.present?
|
43
|
+
|
44
|
+
root.add_namespace_definition(name, url)
|
45
|
+
end
|
46
|
+
|
47
|
+
@all_namespaces_added = true
|
48
|
+
end
|
34
49
|
end
|
35
50
|
|
36
51
|
# Monkey patches for Nokogiri::XML::Node
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Style/Documentation
|
4
|
+
|
5
|
+
# Make debug output more useful (dumping entire document out is not useful)
|
6
|
+
module Nokogiri
|
7
|
+
module XML
|
8
|
+
# rubocop:disable Style/MutableConstant
|
9
|
+
PROFILE_DATA = {}
|
10
|
+
# rubocop:enable Style/MutableConstant
|
11
|
+
|
12
|
+
if ENV['PROFILE']
|
13
|
+
ENV['VERBOSE_PROFILE'] = 1 if ENV['PROFILE'].to_s.downcase == 'verbose'
|
14
|
+
|
15
|
+
# Patches inside Nokogiri to count, time, and print searches. At end of baking
|
16
|
+
# you can `puts Nokogiri::XML::PROFILE_DATA` to see the totals. The counts
|
17
|
+
# hash is defined outside of the if block so that code that prints it doesn't
|
18
|
+
# explode if run without the env var. The `print_profile_data` method is also
|
19
|
+
# provided for a nice printout
|
20
|
+
|
21
|
+
def self.print_profile_data
|
22
|
+
total_duration = 0
|
23
|
+
|
24
|
+
sorted_profile_data = PROFILE_DATA.sort_by { |_, data| data[:time] / data[:count] }.reverse
|
25
|
+
|
26
|
+
puts "\nSearch Profile Data"
|
27
|
+
puts '-----------------------------------------------------------------'
|
28
|
+
puts "#{'Total Time (ms)'.ljust(17)}#{'Avg Time (ms)'.ljust(15)}#{'Count'.ljust(7)}Query"
|
29
|
+
puts '-----------------------------------------------------------------'
|
30
|
+
|
31
|
+
sorted_profile_data.each do |search_path, data|
|
32
|
+
total_time = format('%0.4f', (data[:time] * 1000))
|
33
|
+
avg_time = format('%0.4f', ((data[:time] / data[:count]) * 1000))
|
34
|
+
puts total_time.ljust(17) + avg_time.to_s.ljust(15) + data[:count].to_s.ljust(7) + \
|
35
|
+
search_path
|
36
|
+
total_duration += data[:time] * 1000
|
37
|
+
end
|
38
|
+
|
39
|
+
puts "\nTotal time across all searches (ms): #{total_duration}"
|
40
|
+
end
|
41
|
+
|
42
|
+
class XPathContext
|
43
|
+
alias_method :original_evaluate, :evaluate
|
44
|
+
def evaluate(search_path, handler=nil)
|
45
|
+
puts search_path if ENV['VERBOSE_PROFILE']
|
46
|
+
|
47
|
+
PROFILE_DATA[search_path] ||= Hash.new(0)
|
48
|
+
PROFILE_DATA[search_path][:count] += 1
|
49
|
+
|
50
|
+
start_time = Time.now
|
51
|
+
original_evaluate(search_path, handler).tap do
|
52
|
+
PROFILE_DATA[search_path][:time] += Time.now - start_time
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# rubocop:enable Style/Documentation
|
data/lib/kitchen/search_query.rb
CHANGED
@@ -82,6 +82,12 @@ module Kitchen
|
|
82
82
|
as_type
|
83
83
|
end
|
84
84
|
|
85
|
+
# Returns true if the query has the substitution character ('$')
|
86
|
+
#
|
87
|
+
def expects_substitution?
|
88
|
+
css_or_xpath.nil? || [css_or_xpath].flatten.all? { |item| item.include?('$') }
|
89
|
+
end
|
90
|
+
|
85
91
|
protected
|
86
92
|
|
87
93
|
def condition_passes?(method_or_callable, element, success_outcome)
|
data/lib/kitchen/selector.rb
CHANGED
data/lib/kitchen/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openstax_kitchen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.1.
|
4
|
+
version: 4.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- JP Slavinsky
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-05-
|
11
|
+
date: 2021-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -220,6 +220,7 @@ files:
|
|
220
220
|
- lib/kitchen/chapter_element_enumerator.rb
|
221
221
|
- lib/kitchen/clipboard.rb
|
222
222
|
- lib/kitchen/composite_chapter_element.rb
|
223
|
+
- lib/kitchen/composite_chapter_element_enumerator.rb
|
223
224
|
- lib/kitchen/composite_page_element.rb
|
224
225
|
- lib/kitchen/composite_page_element_enumerator.rb
|
225
226
|
- lib/kitchen/config.rb
|
@@ -320,6 +321,7 @@ files:
|
|
320
321
|
- lib/kitchen/page_element_enumerator.rb
|
321
322
|
- lib/kitchen/pantry.rb
|
322
323
|
- lib/kitchen/patches/nokogiri.rb
|
324
|
+
- lib/kitchen/patches/nokogiri_profiling.rb
|
323
325
|
- lib/kitchen/patches/renderable.rb
|
324
326
|
- lib/kitchen/patches/string.rb
|
325
327
|
- lib/kitchen/recipe.rb
|