openstax_kitchen 3.2.0 → 6.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.
- checksums.yaml +4 -4
- data/.github/workflows/changelog.yml +27 -0
- data/.github/workflows/rubocop.yml +28 -0
- data/.gitignore +1 -1
- data/CHANGELOG.md +94 -1
- data/Gemfile.lock +26 -18
- data/README.md +16 -0
- data/codecov.yaml +1 -0
- data/docker/ci +0 -1
- data/docker/rubocop +22 -0
- data/lib/kitchen/book_document.rb +1 -1
- data/lib/kitchen/book_element.rb +16 -2
- data/lib/kitchen/chapter_element.rb +10 -13
- data/lib/kitchen/chapter_element_enumerator.rb +1 -1
- data/lib/kitchen/composite_chapter_element.rb +7 -11
- data/lib/kitchen/composite_chapter_element_enumerator.rb +21 -0
- data/lib/kitchen/composite_page_element.rb +15 -10
- data/lib/kitchen/composite_page_element_enumerator.rb +1 -1
- data/lib/kitchen/config.rb +14 -0
- data/lib/kitchen/directions/bake_appendix.rb +3 -1
- data/lib/kitchen/directions/bake_chapter_glossary/main.rb +18 -0
- data/lib/kitchen/directions/bake_chapter_glossary/v1.rb +30 -0
- data/lib/kitchen/directions/bake_chapter_introductions.rb +23 -16
- data/lib/kitchen/directions/bake_chapter_introductions/chapter_introduction.xhtml.erb +0 -0
- data/lib/kitchen/directions/bake_chapter_key_concepts/main.rb +7 -2
- data/lib/kitchen/directions/bake_chapter_key_concepts/v1.rb +12 -7
- data/lib/kitchen/directions/bake_chapter_key_equations.rb +26 -21
- data/lib/kitchen/directions/bake_chapter_references/main.rb +16 -0
- data/lib/kitchen/directions/bake_chapter_references/v1.rb +35 -0
- data/lib/kitchen/directions/bake_chapter_section_exercises/main.rb +2 -2
- data/lib/kitchen/directions/bake_chapter_section_exercises/v1.rb +2 -1
- data/lib/kitchen/directions/bake_chapter_solutions/main.rb +11 -0
- data/lib/kitchen/directions/bake_chapter_solutions/v1.rb +37 -0
- data/lib/kitchen/directions/bake_chapter_summary.rb +56 -43
- 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 +2 -2
- data/lib/kitchen/directions/bake_example.rb +8 -1
- data/lib/kitchen/directions/bake_figure.rb +14 -1
- data/lib/kitchen/directions/bake_first_elements.rb +22 -0
- data/lib/kitchen/directions/bake_footnotes/main.rb +2 -2
- data/lib/kitchen/directions/bake_footnotes/v1.rb +13 -9
- data/lib/kitchen/directions/bake_free_response/free_response.xhtml.erb +10 -0
- data/lib/kitchen/directions/{bake_chapter_review → bake_free_response}/main.rb +3 -3
- data/lib/kitchen/directions/bake_free_response/v1.rb +29 -0
- data/lib/kitchen/directions/bake_further_research.rb +61 -0
- data/lib/kitchen/directions/bake_index/v1.rb +36 -26
- data/lib/kitchen/directions/bake_link_placeholders.rb +1 -1
- data/lib/kitchen/directions/bake_notes/bake_note_subtitle.rb +4 -0
- data/lib/kitchen/directions/bake_notes/bake_numbered_notes/main.rb +43 -0
- data/lib/kitchen/directions/bake_notes/bake_numbered_notes/v1.rb +37 -0
- data/lib/kitchen/directions/bake_notes/bake_numbered_notes/v2.rb +25 -0
- data/lib/kitchen/directions/bake_notes/bake_numbered_notes/v3.rb +32 -0
- data/lib/kitchen/directions/bake_numbered_exercise/main.rb +7 -2
- data/lib/kitchen/directions/bake_numbered_exercise/v1.rb +34 -12
- data/lib/kitchen/directions/bake_numbered_table/bake_table_body.rb +29 -0
- data/lib/kitchen/directions/bake_numbered_table/main.rb +4 -0
- data/lib/kitchen/directions/bake_numbered_table/v1.rb +1 -24
- data/lib/kitchen/directions/bake_numbered_table/v2.rb +31 -0
- data/lib/kitchen/directions/bake_page_abstracts.rb +1 -1
- data/lib/kitchen/directions/bake_preface/main.rb +2 -2
- data/lib/kitchen/directions/bake_preface/v1.rb +3 -2
- data/lib/kitchen/directions/bake_references/main.rb +16 -0
- data/lib/kitchen/directions/bake_references/v1.rb +48 -0
- data/lib/kitchen/directions/bake_suggested_reading.rb +5 -0
- data/lib/kitchen/directions/bake_toc.rb +4 -2
- data/lib/kitchen/directions/book_answer_key_container/eob_answer_key_outer_container.xhtml.erb +9 -0
- data/lib/kitchen/directions/book_answer_key_container/main.rb +11 -0
- data/lib/kitchen/directions/book_answer_key_container/v1.rb +14 -0
- data/lib/kitchen/directions/chapter_review_container/chapter_review.xhtml.erb +9 -0
- data/lib/kitchen/directions/chapter_review_container/main.rb +11 -0
- data/lib/kitchen/directions/chapter_review_container/v1.rb +15 -0
- data/lib/kitchen/directions/eoc_section_title_link_snippet.rb +14 -1
- data/lib/kitchen/directions/move_exercises_to_eoc/main.rb +37 -0
- data/lib/kitchen/directions/{bake_chapter_review_exercises → move_exercises_to_eoc}/v1.rb +8 -10
- data/lib/kitchen/directions/{bake_chapter_review_exercises → move_exercises_to_eoc}/v2.rb +8 -9
- data/lib/kitchen/directions/move_exercises_to_eoc/v3.rb +49 -0
- data/lib/kitchen/directions/{bake_chapter_answer_key → move_solutions_to_answer_key}/main.rb +7 -3
- data/lib/kitchen/directions/{bake_chapter_answer_key → move_solutions_to_answer_key}/strategies/calculus.rb +1 -1
- data/lib/kitchen/directions/move_solutions_to_answer_key/strategies/default.rb +27 -0
- data/lib/kitchen/directions/move_solutions_to_answer_key/strategies/precalculus.rb +84 -0
- data/lib/kitchen/directions/{bake_chapter_answer_key → move_solutions_to_answer_key}/strategies/uphysics.rb +7 -5
- data/lib/kitchen/directions/{bake_chapter_answer_key → move_solutions_to_answer_key}/v1.rb +12 -6
- data/lib/kitchen/document.rb +20 -42
- data/lib/kitchen/element.rb +9 -3
- data/lib/kitchen/element_base.rb +108 -21
- data/lib/kitchen/element_enumerator_base.rb +33 -2
- data/lib/kitchen/element_enumerator_factory.rb +28 -12
- data/lib/kitchen/element_factory.rb +3 -3
- data/lib/kitchen/example_element.rb +8 -11
- data/lib/kitchen/example_element_enumerator.rb +1 -1
- data/lib/kitchen/exercise_element.rb +7 -10
- data/lib/kitchen/exercise_element_enumerator.rb +1 -1
- data/lib/kitchen/figure_element.rb +8 -11
- data/lib/kitchen/figure_element_enumerator.rb +1 -1
- data/lib/kitchen/id_tracker.rb +68 -0
- data/lib/kitchen/metadata_element.rb +8 -2
- data/lib/kitchen/metadata_element_enumerator.rb +1 -1
- data/lib/kitchen/note_element.rb +8 -11
- data/lib/kitchen/note_element_enumerator.rb +1 -1
- data/lib/kitchen/oven.rb +5 -1
- data/lib/kitchen/page_element.rb +27 -12
- data/lib/kitchen/page_element_enumerator.rb +1 -1
- data/lib/kitchen/patches/i18n.rb +34 -0
- data/lib/kitchen/patches/integer.rb +24 -0
- data/lib/kitchen/patches/nokogiri.rb +62 -0
- data/lib/kitchen/patches/nokogiri_profiling.rb +60 -0
- data/lib/kitchen/reference_element.rb +27 -0
- data/lib/kitchen/references_element_enumerator.rb +20 -0
- data/lib/kitchen/search_query.rb +31 -3
- data/lib/kitchen/selector.rb +25 -0
- data/lib/kitchen/selectors/base.rb +39 -0
- data/lib/kitchen/selectors/standard_1.rb +13 -0
- data/lib/kitchen/table_element.rb +8 -11
- data/lib/kitchen/table_element_enumerator.rb +1 -1
- data/lib/kitchen/templates/eob_section_title_template.xhtml.erb +10 -0
- data/lib/kitchen/templates/eoc_section_title_template.xhtml.erb +10 -0
- data/lib/kitchen/term_element.rb +5 -8
- data/lib/kitchen/term_element_enumerator.rb +1 -1
- data/lib/kitchen/unit_element.rb +13 -7
- data/lib/kitchen/unit_element_enumerator.rb +1 -1
- data/lib/kitchen/version.rb +1 -1
- data/lib/locales/en.yml +5 -1
- data/lib/locales/es.yml +33 -0
- data/lib/locales/pl.yml +3 -2
- data/lib/openstax_kitchen.rb +2 -5
- data/openstax_kitchen.gemspec +1 -0
- metadata +66 -25
- data/.github/config.yml +0 -14
- data/lib/kitchen/directions/bake_book_answer_key/eob_solutions_container.xhtml.erb +0 -9
- data/lib/kitchen/directions/bake_book_answer_key/main.rb +0 -11
- data/lib/kitchen/directions/bake_book_answer_key/v1.rb +0 -13
- data/lib/kitchen/directions/bake_chapter_glossary.rb +0 -39
- data/lib/kitchen/directions/bake_chapter_key_concepts/key_concepts.xhtml.erb +0 -16
- data/lib/kitchen/directions/bake_chapter_review/chapter_review.xhtml.erb +0 -9
- data/lib/kitchen/directions/bake_chapter_review/v1.rb +0 -13
- data/lib/kitchen/directions/bake_chapter_review_exercises/main.rb +0 -15
- data/lib/kitchen/directions/bake_chapter_review_exercises/review_exercises.xhtml.erb +0 -10
- data/lib/kitchen/directions/bake_exercises/main.rb +0 -12
- data/lib/kitchen/directions/bake_exercises/v1.rb +0 -169
- data/lib/kitchen/directions/bake_notes/bake_notes.rb +0 -48
- data/lib/kitchen/directions/bake_notes/bake_numbered_notes.rb +0 -63
- data/lib/kitchen/directions/bake_problem_first_elements.rb +0 -16
- data/lib/kitchen/transliterations.rb +0 -21
@@ -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
|
@@ -218,11 +241,19 @@ module Kitchen
|
|
218
241
|
except: except)
|
219
242
|
end
|
220
243
|
|
221
|
-
# Returns an enumerator that iterates through
|
244
|
+
# Returns an enumerator that iterates through references within the scope of this enumerator
|
222
245
|
#
|
223
246
|
# @param css_or_xpath [String] additional selectors to further narrow the element iterated over;
|
224
247
|
# a "$" in this argument will be replaced with the default selector for the element being
|
225
248
|
# iterated over.
|
249
|
+
#
|
250
|
+
def references(css_or_xpath=nil)
|
251
|
+
block_error_if(block_given?)
|
252
|
+
chain_to(ReferenceElementEnumerator, css_or_xpath: css_or_xpath)
|
253
|
+
end
|
254
|
+
|
255
|
+
# Returns an enumerator that iterates through exercises within the scope of this enumerator
|
256
|
+
#
|
226
257
|
# @param only [Symbol, Callable] the name of a method to call on an element or a
|
227
258
|
# lambda or proc that accepts an element; elements will only be included in the
|
228
259
|
# search results if the method or callable returns true
|
@@ -12,8 +12,11 @@ module Kitchen
|
|
12
12
|
|
13
13
|
# Creates a new instance
|
14
14
|
#
|
15
|
-
# @param default_css_or_xpath [String] The selectors to substitute for the "$" character
|
16
|
-
# when this factory is used to build an enumerator.
|
15
|
+
# @param default_css_or_xpath [String, Proc, Symbol] The selectors to substitute for the "$" character
|
16
|
+
# when this factory is used to build an enumerator. A string argument is used literally. A proc
|
17
|
+
# is eventually called given the document's Config object (for accessing selectors). A symbol
|
18
|
+
# is interpreted as the name of a selector and is called on the document's Config object's
|
19
|
+
# selectors object. The easiest way to get a Proc is to pass `Selector.named(:name_of_selector)`
|
17
20
|
# @param sub_element_class [ElementBase] The element class to use for what the enumerator finds.
|
18
21
|
# @param enumerator_class [ElementEnumeratorBase] The enumerator class to return
|
19
22
|
# @param detect_sub_element_class [Boolean] If true, infers the sub_element_class from the node
|
@@ -36,14 +39,22 @@ module Kitchen
|
|
36
39
|
# @return [ElementEnumeratorBase] actually returns the concrete enumerator class
|
37
40
|
# given to the factory in its constructor.
|
38
41
|
#
|
39
|
-
def build_within(enumerator_or_element, search_query: SearchQuery.new)
|
40
|
-
search_query.apply_default_css_or_xpath_and_normalize(default_css_or_xpath)
|
41
|
-
|
42
|
+
def build_within(enumerator_or_element, search_query: SearchQuery.new, reload: false)
|
42
43
|
case enumerator_or_element
|
43
44
|
when ElementBase
|
44
|
-
build_within_element(enumerator_or_element,
|
45
|
+
build_within_element(enumerator_or_element,
|
46
|
+
search_query: search_query,
|
47
|
+
reload: reload)
|
45
48
|
when ElementEnumeratorBase
|
46
|
-
|
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)
|
47
58
|
end
|
48
59
|
end
|
49
60
|
|
@@ -55,7 +66,7 @@ module Kitchen
|
|
55
66
|
#
|
56
67
|
def or_with(other_factory)
|
57
68
|
self.class.new(
|
58
|
-
default_css_or_xpath:
|
69
|
+
default_css_or_xpath: [default_css_or_xpath, other_factory.default_css_or_xpath],
|
59
70
|
enumerator_class: TypeCastingElementEnumerator,
|
60
71
|
detect_sub_element_class: true
|
61
72
|
)
|
@@ -63,7 +74,10 @@ module Kitchen
|
|
63
74
|
|
64
75
|
protected
|
65
76
|
|
66
|
-
def build_within_element(element, search_query:)
|
77
|
+
def build_within_element(element, search_query:, reload:)
|
78
|
+
search_query.apply_default_css_or_xpath_and_normalize(default_css_or_xpath,
|
79
|
+
config: element.config)
|
80
|
+
|
67
81
|
enumerator_class.new(search_query: search_query) do |block|
|
68
82
|
grand_ancestors = element.ancestors
|
69
83
|
parent_ancestor = Ancestor.new(element)
|
@@ -73,7 +87,7 @@ module Kitchen
|
|
73
87
|
# below, the counts are correct.
|
74
88
|
element.uncount(search_query)
|
75
89
|
|
76
|
-
element.
|
90
|
+
element.raw_search(*search_query.css_or_xpath, reload: reload).each do |sub_node|
|
77
91
|
sub_element = ElementFactory.build_from_node(
|
78
92
|
node: sub_node,
|
79
93
|
document: element.document,
|
@@ -101,13 +115,15 @@ module Kitchen
|
|
101
115
|
end
|
102
116
|
end
|
103
117
|
|
104
|
-
def build_within_other_enumerator(other_enumerator, search_query:)
|
118
|
+
def build_within_other_enumerator(other_enumerator, search_query:, reload:)
|
105
119
|
# Return a new enumerator instance that internally iterates over `other_enumerator`
|
106
120
|
# running a new enumerator for each element returned by that other enumerator.
|
107
121
|
enumerator_class.new(search_query: search_query,
|
108
122
|
upstream_enumerator: other_enumerator) do |block|
|
109
123
|
other_enumerator.each do |element|
|
110
|
-
build_within_element(element,
|
124
|
+
build_within_element(element,
|
125
|
+
search_query: search_query,
|
126
|
+
reload: reload).each do |sub_element|
|
111
127
|
block.yield(sub_element)
|
112
128
|
end
|
113
129
|
end
|
@@ -25,7 +25,7 @@ module Kitchen
|
|
25
25
|
element_class: nil,
|
26
26
|
default_short_type: nil,
|
27
27
|
detect_element_class: false)
|
28
|
-
element_class ||= detect_element_class ?
|
28
|
+
element_class ||= detect_element_class ? find_element_class(node, document.config) : Element
|
29
29
|
|
30
30
|
if element_class == Element
|
31
31
|
element_class.new(node: node,
|
@@ -37,9 +37,9 @@ module Kitchen
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
def self.
|
40
|
+
def self.find_element_class(node, config)
|
41
41
|
ELEMENT_CLASSES.find do |klass|
|
42
|
-
klass.is_the_element_class_for?(node)
|
42
|
+
klass.is_the_element_class_for?(node, config: config)
|
43
43
|
end || Element
|
44
44
|
end
|
45
45
|
|
@@ -13,8 +13,14 @@ module Kitchen
|
|
13
13
|
def initialize(node:, document: nil)
|
14
14
|
super(node: node,
|
15
15
|
document: document,
|
16
|
-
enumerator_class: ExampleElementEnumerator
|
17
|
-
|
16
|
+
enumerator_class: ExampleElementEnumerator)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the short type
|
20
|
+
# @return [Symbol]
|
21
|
+
#
|
22
|
+
def self.short_type
|
23
|
+
:example
|
18
24
|
end
|
19
25
|
|
20
26
|
# Returns the an enumerator for titles.
|
@@ -25,14 +31,5 @@ module Kitchen
|
|
25
31
|
search("span[data-type='title']")
|
26
32
|
end
|
27
33
|
|
28
|
-
# Returns true if this class represents the element for the given node
|
29
|
-
#
|
30
|
-
# @param node [Nokogiri::XML::Node] the underlying node
|
31
|
-
# @return [Boolean]
|
32
|
-
#
|
33
|
-
def self.is_the_element_class_for?(node)
|
34
|
-
node['data-type'] == 'example'
|
35
|
-
end
|
36
|
-
|
37
34
|
end
|
38
35
|
end
|
@@ -11,7 +11,7 @@ module Kitchen
|
|
11
11
|
#
|
12
12
|
def self.factory
|
13
13
|
ElementEnumeratorFactory.new(
|
14
|
-
default_css_or_xpath:
|
14
|
+
default_css_or_xpath: Selector.named(:example),
|
15
15
|
sub_element_class: ExampleElement,
|
16
16
|
enumerator_class: self
|
17
17
|
)
|
@@ -13,17 +13,14 @@ module Kitchen
|
|
13
13
|
def initialize(node:, document: nil)
|
14
14
|
super(node: node,
|
15
15
|
document: document,
|
16
|
-
enumerator_class: ExerciseElementEnumerator
|
17
|
-
short_type: :exercise)
|
16
|
+
enumerator_class: ExerciseElementEnumerator)
|
18
17
|
end
|
19
18
|
|
20
|
-
# Returns
|
19
|
+
# Returns the short type
|
20
|
+
# @return [Symbol]
|
21
21
|
#
|
22
|
-
|
23
|
-
|
24
|
-
#
|
25
|
-
def self.is_the_element_class_for?(node)
|
26
|
-
node['data-type'] == 'exercise'
|
22
|
+
def self.short_type
|
23
|
+
:exercise
|
27
24
|
end
|
28
25
|
|
29
26
|
# Returns the enumerator for problem.
|
@@ -31,7 +28,7 @@ module Kitchen
|
|
31
28
|
# @return ElementEnumerator
|
32
29
|
#
|
33
30
|
def problem
|
34
|
-
first("[data-type='problem']")
|
31
|
+
first("div[data-type='problem']")
|
35
32
|
end
|
36
33
|
|
37
34
|
# Returns the enumerator for solution.
|
@@ -39,7 +36,7 @@ module Kitchen
|
|
39
36
|
# @return ElementEnumerator
|
40
37
|
#
|
41
38
|
def solution
|
42
|
-
first("[data-type='solution']")
|
39
|
+
first("div[data-type='solution']")
|
43
40
|
end
|
44
41
|
end
|
45
42
|
end
|
@@ -11,7 +11,7 @@ module Kitchen
|
|
11
11
|
#
|
12
12
|
def self.factory
|
13
13
|
ElementEnumeratorFactory.new(
|
14
|
-
default_css_or_xpath:
|
14
|
+
default_css_or_xpath: Selector.named(:exercise),
|
15
15
|
sub_element_class: ExerciseElement,
|
16
16
|
enumerator_class: self
|
17
17
|
)
|
@@ -13,8 +13,14 @@ module Kitchen
|
|
13
13
|
def initialize(node:, document: nil)
|
14
14
|
super(node: node,
|
15
15
|
document: document,
|
16
|
-
enumerator_class: FigureElementEnumerator
|
17
|
-
|
16
|
+
enumerator_class: FigureElementEnumerator)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the short type
|
20
|
+
# @return [Symbol]
|
21
|
+
#
|
22
|
+
def self.short_type
|
23
|
+
:figure
|
18
24
|
end
|
19
25
|
|
20
26
|
# Returns the caption element
|
@@ -41,14 +47,5 @@ module Kitchen
|
|
41
47
|
parent.name == 'figure'
|
42
48
|
end
|
43
49
|
|
44
|
-
# Returns true if this class represents the element for the given node
|
45
|
-
#
|
46
|
-
# @param node [Nokogiri::XML::Node] the underlying node
|
47
|
-
# @return [Boolean]
|
48
|
-
#
|
49
|
-
def self.is_the_element_class_for?(node)
|
50
|
-
node.name == 'figure'
|
51
|
-
end
|
52
|
-
|
53
50
|
end
|
54
51
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kitchen
|
4
|
+
# A class to track and modify duplicate IDs in a document
|
5
|
+
#
|
6
|
+
class IdTracker
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@id_data = Hash.new { |hash, key| hash[key] = { count: 0, last_pasted: false } }
|
10
|
+
@id_copy_suffix = '_copy_'
|
11
|
+
end
|
12
|
+
|
13
|
+
# Keeps track that an element with the given ID has been copied. When such
|
14
|
+
# elements are pasted, this information is used to give those elements unique
|
15
|
+
# IDs that don't duplicate the original element.
|
16
|
+
#
|
17
|
+
# @param id [String] the ID
|
18
|
+
#
|
19
|
+
def record_id_copied(id)
|
20
|
+
return if id.blank?
|
21
|
+
|
22
|
+
@id_data[id][:count] += 1
|
23
|
+
@id_data[id][:last_pasted] = false
|
24
|
+
end
|
25
|
+
|
26
|
+
# Keeps track that an element with the given ID has been cut.
|
27
|
+
#
|
28
|
+
# @param id [String]
|
29
|
+
#
|
30
|
+
def record_id_cut(id)
|
31
|
+
return if id.blank?
|
32
|
+
|
33
|
+
@id_data[id][:count] -= 1 if @id_data[id][:count].positive?
|
34
|
+
@id_data[id][:last_pasted] = false
|
35
|
+
end
|
36
|
+
|
37
|
+
# Keeps track that an element with the given ID has been pasted.
|
38
|
+
#
|
39
|
+
# @param id [String]
|
40
|
+
#
|
41
|
+
def record_id_pasted(id)
|
42
|
+
return if id.blank?
|
43
|
+
|
44
|
+
@id_data[id][:count] += 1 if @id_data[id][:last_pasted]
|
45
|
+
@id_data[id][:last_pasted] = true
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns a unique ID given the ID of an element that was copied and is about
|
49
|
+
# to be pasted
|
50
|
+
#
|
51
|
+
# @param original_id [String]
|
52
|
+
# @return [String]
|
53
|
+
#
|
54
|
+
def modified_id_to_paste(original_id)
|
55
|
+
return nil if original_id.nil?
|
56
|
+
return '' if original_id.blank?
|
57
|
+
|
58
|
+
count = @id_data[original_id][:count]
|
59
|
+
# A count of 0 means the element was cut and this is the first paste, do not
|
60
|
+
# modify the ID; otherwise, use the uniquified ID.
|
61
|
+
if count.zero?
|
62
|
+
original_id
|
63
|
+
else
|
64
|
+
"#{original_id}#{@id_copy_suffix}#{count}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -12,8 +12,14 @@ module Kitchen
|
|
12
12
|
def initialize(node:, document: nil)
|
13
13
|
super(node: node,
|
14
14
|
document: document,
|
15
|
-
enumerator_class: MetadataElementEnumerator
|
16
|
-
|
15
|
+
enumerator_class: MetadataElementEnumerator)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns the short type
|
19
|
+
# @return [Symbol]
|
20
|
+
#
|
21
|
+
def self.short_type
|
22
|
+
:metadata
|
17
23
|
end
|
18
24
|
|
19
25
|
# Returns set of selected data elements
|
data/lib/kitchen/note_element.rb
CHANGED
@@ -13,8 +13,14 @@ module Kitchen
|
|
13
13
|
def initialize(node:, document: nil)
|
14
14
|
super(node: node,
|
15
15
|
document: document,
|
16
|
-
enumerator_class: NoteElementEnumerator
|
17
|
-
|
16
|
+
enumerator_class: NoteElementEnumerator)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the short type
|
20
|
+
# @return [Symbol]
|
21
|
+
#
|
22
|
+
def self.short_type
|
23
|
+
:note
|
18
24
|
end
|
19
25
|
|
20
26
|
# Returns the note's title element
|
@@ -46,15 +52,6 @@ module Kitchen
|
|
46
52
|
end
|
47
53
|
end
|
48
54
|
|
49
|
-
# Returns true if this class represents the element for the given node
|
50
|
-
#
|
51
|
-
# @param node [Nokogiri::XML::Node] the underlying node
|
52
|
-
# @return [Boolean]
|
53
|
-
#
|
54
|
-
def self.is_the_element_class_for?(node)
|
55
|
-
node['data-type'] == 'note'
|
56
|
-
end
|
57
|
-
|
58
55
|
protected
|
59
56
|
|
60
57
|
def detected_note_title_key
|
@@ -11,7 +11,7 @@ module Kitchen
|
|
11
11
|
#
|
12
12
|
def self.factory
|
13
13
|
ElementEnumeratorFactory.new(
|
14
|
-
default_css_or_xpath:
|
14
|
+
default_css_or_xpath: Selector.named(:note),
|
15
15
|
sub_element_class: NoteElement,
|
16
16
|
enumerator_class: self
|
17
17
|
)
|
data/lib/kitchen/oven.rb
CHANGED
@@ -28,6 +28,8 @@ module Kitchen
|
|
28
28
|
config: config
|
29
29
|
)
|
30
30
|
|
31
|
+
I18n.locale = doc.locale
|
32
|
+
|
31
33
|
[recipes].flatten.each do |recipe|
|
32
34
|
recipe.document = doc
|
33
35
|
recipe.bake
|
@@ -35,10 +37,12 @@ module Kitchen
|
|
35
37
|
profile.baked!
|
36
38
|
|
37
39
|
File.open(output_file, 'w') do |f|
|
38
|
-
f.write doc.to_xhtml(indent: 2)
|
40
|
+
f.write doc.to_xhtml(indent: 2, encoding: doc.encoding || 'utf-8')
|
39
41
|
end
|
40
42
|
profile.written!
|
41
43
|
|
44
|
+
Nokogiri::XML.print_profile_data if ENV['PROFILE'] && !ENV['TESTING']
|
45
|
+
|
42
46
|
profile
|
43
47
|
end
|
44
48
|
|