openstax_kitchen 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.devcontainer/devcontainer.json +19 -0
- data/.github/workflows/tests.yml +36 -0
- data/.gitignore +20 -0
- data/.rspec +3 -0
- data/.solargraph.yml +15 -0
- data/CHANGELOG.md +11 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Dockerfile +19 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +74 -0
- data/LICENSE.txt +21 -0
- data/README.md +674 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/normalize +79 -0
- data/bin/setup +8 -0
- data/books/chemistry2e/bake.rb +133 -0
- data/codecov.yaml +27 -0
- data/docker-compose.yml +12 -0
- data/docker/bash +1 -0
- data/docker/entrypoint +9 -0
- data/lib/kitchen.rb +57 -0
- data/lib/kitchen/ancestor.rb +30 -0
- data/lib/kitchen/book_document.rb +18 -0
- data/lib/kitchen/book_element.rb +24 -0
- data/lib/kitchen/book_element_enumerator.rb +5 -0
- data/lib/kitchen/book_recipe.rb +25 -0
- data/lib/kitchen/chapter_element.rb +35 -0
- data/lib/kitchen/chapter_element_enumerator.rb +13 -0
- data/lib/kitchen/clipboard.rb +37 -0
- data/lib/kitchen/composite_chapter_element.rb +23 -0
- data/lib/kitchen/composite_page_element.rb +27 -0
- data/lib/kitchen/composite_page_element_enumerator.rb +13 -0
- data/lib/kitchen/config.rb +20 -0
- data/lib/kitchen/counter.rb +34 -0
- data/lib/kitchen/debug/print_recipe_error.rb +80 -0
- data/lib/kitchen/directions/bake_appendix.rb +26 -0
- data/lib/kitchen/directions/bake_chapter_glossary.rb +34 -0
- data/lib/kitchen/directions/bake_chapter_introductions.rb +58 -0
- data/lib/kitchen/directions/bake_chapter_key_equations.rb +30 -0
- data/lib/kitchen/directions/bake_chapter_summary.rb +52 -0
- data/lib/kitchen/directions/bake_composite_pages.rb +13 -0
- data/lib/kitchen/directions/bake_example.rb +31 -0
- data/lib/kitchen/directions/bake_exercises.rb +164 -0
- data/lib/kitchen/directions/bake_figure.rb +25 -0
- data/lib/kitchen/directions/bake_footnotes/main.rb +11 -0
- data/lib/kitchen/directions/bake_footnotes/v1.rb +38 -0
- data/lib/kitchen/directions/bake_index/main.rb +11 -0
- data/lib/kitchen/directions/bake_index/v1.rb +138 -0
- data/lib/kitchen/directions/bake_index/v1.xhtml.erb +28 -0
- data/lib/kitchen/directions/bake_math_in_paragraph.rb +13 -0
- data/lib/kitchen/directions/bake_notes.rb +58 -0
- data/lib/kitchen/directions/bake_numbered_table/main.rb +11 -0
- data/lib/kitchen/directions/bake_numbered_table/v1.rb +47 -0
- data/lib/kitchen/directions/bake_stepwise.rb +27 -0
- data/lib/kitchen/directions/bake_toc.rb +103 -0
- data/lib/kitchen/directions/bake_unnumbered_tables.rb +14 -0
- data/lib/kitchen/directions/move_title_text_into_span.rb +15 -0
- data/lib/kitchen/document.rb +142 -0
- data/lib/kitchen/element.rb +15 -0
- data/lib/kitchen/element_base.rb +444 -0
- data/lib/kitchen/element_enumerator.rb +12 -0
- data/lib/kitchen/element_enumerator_base.rb +101 -0
- data/lib/kitchen/element_enumerator_factory.rb +111 -0
- data/lib/kitchen/element_factory.rb +32 -0
- data/lib/kitchen/errors.rb +4 -0
- data/lib/kitchen/example_element.rb +20 -0
- data/lib/kitchen/example_element_enumerator.rb +13 -0
- data/lib/kitchen/figure_element.rb +20 -0
- data/lib/kitchen/figure_element_enumerator.rb +13 -0
- data/lib/kitchen/mixins/block_error_if.rb +19 -0
- data/lib/kitchen/note_element.rb +43 -0
- data/lib/kitchen/note_element_enumerator.rb +13 -0
- data/lib/kitchen/oven.rb +61 -0
- data/lib/kitchen/page_element.rb +51 -0
- data/lib/kitchen/page_element_enumerator.rb +13 -0
- data/lib/kitchen/pantry.rb +35 -0
- data/lib/kitchen/patches/nokogiri.rb +31 -0
- data/lib/kitchen/patches/renderable.rb +31 -0
- data/lib/kitchen/patches/string.rb +5 -0
- data/lib/kitchen/recipe.rb +78 -0
- data/lib/kitchen/search_history.rb +33 -0
- data/lib/kitchen/selectors/base.rb +8 -0
- data/lib/kitchen/selectors/standard_1.rb +12 -0
- data/lib/kitchen/table_element.rb +36 -0
- data/lib/kitchen/table_element_enumerator.rb +13 -0
- data/lib/kitchen/term_element.rb +16 -0
- data/lib/kitchen/term_element_enumerator.rb +13 -0
- data/lib/kitchen/transliterations.rb +19 -0
- data/lib/kitchen/type_casting_element_enumerator.rb +23 -0
- data/lib/kitchen/utils.rb +19 -0
- data/lib/kitchen/version.rb +3 -0
- data/lib/locales/en.yml +21 -0
- data/lib/notes.md +9 -0
- data/openstax_kitchen.gemspec +39 -0
- data/tutorials/00/expected_baked.html +3 -0
- data/tutorials/00/raw.html +3 -0
- data/tutorials/00/solution_1.rb +7 -0
- data/tutorials/00/solution_2.rb +6 -0
- data/tutorials/01/expected_baked.html +66 -0
- data/tutorials/01/raw.html +62 -0
- data/tutorials/01/solution_1.rb +16 -0
- data/tutorials/01/solution_2.rb +24 -0
- data/tutorials/02/expected_baked.html +207 -0
- data/tutorials/02/raw.html +201 -0
- data/tutorials/02/solution_1.rb +29 -0
- data/tutorials/03/expected_baked.html +33 -0
- data/tutorials/03/raw.html +31 -0
- data/tutorials/03/solution_1.rb +16 -0
- data/tutorials/03/solution_2.rb +15 -0
- data/tutorials/04/expected_baked.html +36 -0
- data/tutorials/04/raw.html +36 -0
- data/tutorials/04/solution_1.rb +20 -0
- data/tutorials/04/solution_2.rb +25 -0
- data/tutorials/05/expected_baked.html +11 -0
- data/tutorials/05/raw.html +11 -0
- data/tutorials/05/solution_1.rb +9 -0
- data/tutorials/check_it +64 -0
- data/tutorials/setup_my_recipes +30 -0
- metadata +278 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
module Kitchen
|
2
|
+
class PageElement < ElementBase
|
3
|
+
|
4
|
+
def initialize(node:, document: nil)
|
5
|
+
super(node: node,
|
6
|
+
document: document,
|
7
|
+
enumerator_class: PageElementEnumerator,
|
8
|
+
short_type: :page)
|
9
|
+
end
|
10
|
+
|
11
|
+
def title
|
12
|
+
# The selector for intro titles changes during the baking process
|
13
|
+
first!(is_introduction? ?
|
14
|
+
selectors.title_in_introduction_page :
|
15
|
+
selectors.title_in_page)
|
16
|
+
end
|
17
|
+
|
18
|
+
def is_introduction?
|
19
|
+
has_class?("introduction")
|
20
|
+
end
|
21
|
+
|
22
|
+
def is_preface?
|
23
|
+
has_class?("preface")
|
24
|
+
end
|
25
|
+
|
26
|
+
def is_appendix?
|
27
|
+
has_class?("appendix")
|
28
|
+
end
|
29
|
+
|
30
|
+
def metadata
|
31
|
+
first!("div[data-type='metadata']")
|
32
|
+
end
|
33
|
+
|
34
|
+
def summary
|
35
|
+
first!("section.summary")
|
36
|
+
end
|
37
|
+
|
38
|
+
def exercises
|
39
|
+
first!("section.exercises")
|
40
|
+
end
|
41
|
+
|
42
|
+
def exercises_section
|
43
|
+
search("")
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.is_the_element_class_for?(node)
|
47
|
+
node['data-type'] == "page"
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Kitchen
|
2
|
+
class PageElementEnumerator < ElementEnumeratorBase
|
3
|
+
|
4
|
+
def self.factory
|
5
|
+
ElementEnumeratorFactory.new(
|
6
|
+
default_css_or_xpath: "div[data-type='page']", # TODO get from config?
|
7
|
+
sub_element_class: PageElement,
|
8
|
+
enumerator_class: self
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Kitchen
|
2
|
+
# A place to store labeled items during recipe work. Essentially, a slightly
|
3
|
+
# improved hash.
|
4
|
+
#
|
5
|
+
class Pantry
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def store(item, label:)
|
9
|
+
@hash[label.to_sym] = item
|
10
|
+
end
|
11
|
+
|
12
|
+
def get(label)
|
13
|
+
@hash[label.to_sym]
|
14
|
+
end
|
15
|
+
|
16
|
+
def get!(label)
|
17
|
+
get(label) || raise(RecipeError, "There is no pantry item labeled '#{label}'")
|
18
|
+
end
|
19
|
+
|
20
|
+
def each(&block)
|
21
|
+
@hash.each{|k,v| block.call(k,v)}
|
22
|
+
end
|
23
|
+
|
24
|
+
def size
|
25
|
+
@hash.keys.size
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
@hash = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Make debug output more useful (dumping entire document out is not useful)
|
2
|
+
module Nokogiri
|
3
|
+
module XML
|
4
|
+
class Document
|
5
|
+
def inspect
|
6
|
+
"Nokogiri::XML::Document <hidden for brevity>"
|
7
|
+
end
|
8
|
+
|
9
|
+
def alphabetize_attributes!
|
10
|
+
traverse do |child|
|
11
|
+
next if child.text? || child.document?
|
12
|
+
child_attributes = child.attributes
|
13
|
+
child_attributes.each do |key, value|
|
14
|
+
child.remove_attribute(key)
|
15
|
+
end
|
16
|
+
sorted_keys = child_attributes.keys.sort
|
17
|
+
sorted_keys.each do |key|
|
18
|
+
value = child_attributes[key].to_s
|
19
|
+
child[key] = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Node
|
26
|
+
def inspect
|
27
|
+
to_s
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Object
|
2
|
+
|
3
|
+
# Adds a `render` method to a class for rendering an ERB template to a string.
|
4
|
+
def self.renderable(dir: nil)
|
5
|
+
dir ||= begin
|
6
|
+
this_patch_file = __FILE__
|
7
|
+
this_patch_file_caller_index = caller_locations.find_index do |location|
|
8
|
+
location.absolute_path == this_patch_file
|
9
|
+
end
|
10
|
+
|
11
|
+
location_that_called_renderable = caller_locations[(this_patch_file_caller_index || -1)+1]
|
12
|
+
File.dirname(location_that_called_renderable.path)
|
13
|
+
end
|
14
|
+
|
15
|
+
class_eval <<~METHOD
|
16
|
+
def renderable_base_dir
|
17
|
+
"#{dir}"
|
18
|
+
end
|
19
|
+
METHOD
|
20
|
+
|
21
|
+
class_eval do
|
22
|
+
def render(file:)
|
23
|
+
file = File.absolute_path(file, renderable_base_dir)
|
24
|
+
template = File.open(file, 'rb', &:read)
|
25
|
+
ERB.new(template).result(binding)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Kitchen
|
2
|
+
class Recipe
|
3
|
+
|
4
|
+
attr_reader :document
|
5
|
+
attr_reader :source_location
|
6
|
+
|
7
|
+
def document=(document)
|
8
|
+
@document =
|
9
|
+
case document
|
10
|
+
when Kitchen::Document
|
11
|
+
document
|
12
|
+
else
|
13
|
+
raise "Unsupported document type `#{document.class}`"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Make a new Recipe
|
18
|
+
#
|
19
|
+
# @yieldparam doc [Document] an object representing an XML document
|
20
|
+
#
|
21
|
+
def initialize(&block)
|
22
|
+
@source_location = block.source_location[0]
|
23
|
+
@block = block
|
24
|
+
end
|
25
|
+
|
26
|
+
def node!
|
27
|
+
node || raise("The recipe's node has not been set")
|
28
|
+
end
|
29
|
+
|
30
|
+
def bake
|
31
|
+
begin
|
32
|
+
@block.to_proc.call(document)
|
33
|
+
rescue RecipeError => ee
|
34
|
+
print_recipe_error_and_exit(ee)
|
35
|
+
rescue ArgumentError => ee
|
36
|
+
if if_any_stack_file_matches_source_location?(ee)
|
37
|
+
print_recipe_error_and_exit(ee)
|
38
|
+
else
|
39
|
+
raise
|
40
|
+
end
|
41
|
+
rescue NoMethodError => ee
|
42
|
+
if if_any_stack_file_matches_source_location?(ee)
|
43
|
+
print_recipe_error_and_exit(ee)
|
44
|
+
else
|
45
|
+
raise
|
46
|
+
end
|
47
|
+
rescue NameError => ee
|
48
|
+
if if_stack_starts_with_source_location?(ee)
|
49
|
+
print_recipe_error_and_exit(ee)
|
50
|
+
else
|
51
|
+
raise
|
52
|
+
end
|
53
|
+
rescue ElementNotFoundError => ee
|
54
|
+
print_recipe_error_and_exit(ee)
|
55
|
+
rescue Nokogiri::CSS::SyntaxError => ee
|
56
|
+
print_recipe_error_and_exit(ee)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
def if_stack_starts_with_source_location?(error)
|
63
|
+
error.backtrace.first.start_with?(source_location)
|
64
|
+
end
|
65
|
+
|
66
|
+
def if_any_stack_file_matches_source_location?(error)
|
67
|
+
error.backtrace.any? {|entry| entry.start_with?(@source_location)}
|
68
|
+
end
|
69
|
+
|
70
|
+
def print_recipe_error_and_exit(error)
|
71
|
+
Kitchen::Debug.print_recipe_error(error: error,
|
72
|
+
source_location: source_location,
|
73
|
+
document: document)
|
74
|
+
exit(1)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Kitchen
|
2
|
+
class SearchHistory
|
3
|
+
attr_reader :latest
|
4
|
+
attr_reader :upstream
|
5
|
+
|
6
|
+
def self.empty
|
7
|
+
new
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(css_or_xpath)
|
11
|
+
self.class.new(self, css_or_xpath.nil? ? nil : [css_or_xpath].join(", "))
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s(missing_string="?")
|
15
|
+
to_a.map{|item| "[#{item || missing_string}]"}.join(" ")
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_a
|
19
|
+
empty? ? [] : [upstream&.to_a || [], latest].flatten
|
20
|
+
end
|
21
|
+
|
22
|
+
def empty?
|
23
|
+
upstream.nil? && latest.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def initialize(upstream=nil, latest=nil)
|
29
|
+
@upstream = upstream
|
30
|
+
@latest = latest
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Kitchen
|
2
|
+
class TableElement < ElementBase
|
3
|
+
|
4
|
+
def initialize(node:, document: nil)
|
5
|
+
super(node: node,
|
6
|
+
document: document,
|
7
|
+
enumerator_class: TableElementEnumerator,
|
8
|
+
short_type: :table)
|
9
|
+
end
|
10
|
+
|
11
|
+
def title_row
|
12
|
+
top_titled? ? first('thead').first('tr') : nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def title
|
16
|
+
title_row&.first('th').children
|
17
|
+
end
|
18
|
+
|
19
|
+
def top_titled?
|
20
|
+
has_class?('top-titled')
|
21
|
+
end
|
22
|
+
|
23
|
+
def unnumbered?
|
24
|
+
has_class?('unnumbered')
|
25
|
+
end
|
26
|
+
|
27
|
+
def caption
|
28
|
+
first("caption")
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.is_the_element_class_for?(node)
|
32
|
+
node.name == "table"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Kitchen
|
2
|
+
class TableElementEnumerator < ElementEnumeratorBase
|
3
|
+
|
4
|
+
def self.factory
|
5
|
+
ElementEnumeratorFactory.new(
|
6
|
+
default_css_or_xpath: "table", # TODO get from config?
|
7
|
+
sub_element_class: TableElement,
|
8
|
+
enumerator_class: self
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Kitchen
|
2
|
+
class TermElement < ElementBase
|
3
|
+
|
4
|
+
def initialize(node:, document: nil)
|
5
|
+
super(node: node,
|
6
|
+
document: document,
|
7
|
+
enumerator_class: TermElementEnumerator,
|
8
|
+
short_type: :term)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.is_the_element_class_for?(node)
|
12
|
+
node['data-type'] == "term"
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Kitchen
|
2
|
+
class TermElementEnumerator < ElementEnumeratorBase
|
3
|
+
|
4
|
+
def self.factory
|
5
|
+
ElementEnumeratorFactory.new(
|
6
|
+
default_css_or_xpath: "span[data-type='term']", # TODO get from config?
|
7
|
+
sub_element_class: TermElement,
|
8
|
+
enumerator_class: self
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Kitchen
|
2
|
+
# These are added to every translation locale, including the `test` locale
|
3
|
+
# set by `stub_locales`. When we sort strings with accent marks, we use
|
4
|
+
# `ActiveSupport::Inflector.transliterate` to ensure that the sorting is
|
5
|
+
# sensible. This method does not know about Greek characters by default so
|
6
|
+
# we teach it about them by adding the rules below to the i18n configuration.
|
7
|
+
|
8
|
+
TRANSLITERATIONS = {
|
9
|
+
i18n: {
|
10
|
+
transliterate: {
|
11
|
+
rule: {
|
12
|
+
σ: "σ",
|
13
|
+
Δ: "Δ",
|
14
|
+
π: "π",
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Kitchen
|
2
|
+
class TypeCastingElementEnumerator < ElementEnumeratorBase
|
3
|
+
|
4
|
+
def self.factory
|
5
|
+
ElementEnumeratorFactory.new(
|
6
|
+
enumerator_class: self,
|
7
|
+
detect_sub_element_class: true
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
def only(*element_classes)
|
12
|
+
element_classes.flatten!
|
13
|
+
|
14
|
+
TypeCastingElementEnumerator.new do |block|
|
15
|
+
self.each do |element|
|
16
|
+
next unless element_classes.include?(element.class)
|
17
|
+
block.yield(element)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Kitchen
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
def self.search_path_to_type(search_path)
|
5
|
+
[search_path].flatten.join(",")
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.normalized_xhtml_string(xml_thing_with_to_s)
|
9
|
+
doc = Nokogiri::XML(xml_thing_with_to_s.to_s) do |config|
|
10
|
+
config.noblanks
|
11
|
+
end
|
12
|
+
|
13
|
+
doc.alphabetize_attributes!
|
14
|
+
|
15
|
+
doc.to_xhtml(indent: 2)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|