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.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/.devcontainer/devcontainer.json +19 -0
  3. data/.github/workflows/tests.yml +36 -0
  4. data/.gitignore +20 -0
  5. data/.rspec +3 -0
  6. data/.solargraph.yml +15 -0
  7. data/CHANGELOG.md +11 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Dockerfile +19 -0
  10. data/Gemfile +9 -0
  11. data/Gemfile.lock +74 -0
  12. data/LICENSE.txt +21 -0
  13. data/README.md +674 -0
  14. data/Rakefile +6 -0
  15. data/bin/console +14 -0
  16. data/bin/normalize +79 -0
  17. data/bin/setup +8 -0
  18. data/books/chemistry2e/bake.rb +133 -0
  19. data/codecov.yaml +27 -0
  20. data/docker-compose.yml +12 -0
  21. data/docker/bash +1 -0
  22. data/docker/entrypoint +9 -0
  23. data/lib/kitchen.rb +57 -0
  24. data/lib/kitchen/ancestor.rb +30 -0
  25. data/lib/kitchen/book_document.rb +18 -0
  26. data/lib/kitchen/book_element.rb +24 -0
  27. data/lib/kitchen/book_element_enumerator.rb +5 -0
  28. data/lib/kitchen/book_recipe.rb +25 -0
  29. data/lib/kitchen/chapter_element.rb +35 -0
  30. data/lib/kitchen/chapter_element_enumerator.rb +13 -0
  31. data/lib/kitchen/clipboard.rb +37 -0
  32. data/lib/kitchen/composite_chapter_element.rb +23 -0
  33. data/lib/kitchen/composite_page_element.rb +27 -0
  34. data/lib/kitchen/composite_page_element_enumerator.rb +13 -0
  35. data/lib/kitchen/config.rb +20 -0
  36. data/lib/kitchen/counter.rb +34 -0
  37. data/lib/kitchen/debug/print_recipe_error.rb +80 -0
  38. data/lib/kitchen/directions/bake_appendix.rb +26 -0
  39. data/lib/kitchen/directions/bake_chapter_glossary.rb +34 -0
  40. data/lib/kitchen/directions/bake_chapter_introductions.rb +58 -0
  41. data/lib/kitchen/directions/bake_chapter_key_equations.rb +30 -0
  42. data/lib/kitchen/directions/bake_chapter_summary.rb +52 -0
  43. data/lib/kitchen/directions/bake_composite_pages.rb +13 -0
  44. data/lib/kitchen/directions/bake_example.rb +31 -0
  45. data/lib/kitchen/directions/bake_exercises.rb +164 -0
  46. data/lib/kitchen/directions/bake_figure.rb +25 -0
  47. data/lib/kitchen/directions/bake_footnotes/main.rb +11 -0
  48. data/lib/kitchen/directions/bake_footnotes/v1.rb +38 -0
  49. data/lib/kitchen/directions/bake_index/main.rb +11 -0
  50. data/lib/kitchen/directions/bake_index/v1.rb +138 -0
  51. data/lib/kitchen/directions/bake_index/v1.xhtml.erb +28 -0
  52. data/lib/kitchen/directions/bake_math_in_paragraph.rb +13 -0
  53. data/lib/kitchen/directions/bake_notes.rb +58 -0
  54. data/lib/kitchen/directions/bake_numbered_table/main.rb +11 -0
  55. data/lib/kitchen/directions/bake_numbered_table/v1.rb +47 -0
  56. data/lib/kitchen/directions/bake_stepwise.rb +27 -0
  57. data/lib/kitchen/directions/bake_toc.rb +103 -0
  58. data/lib/kitchen/directions/bake_unnumbered_tables.rb +14 -0
  59. data/lib/kitchen/directions/move_title_text_into_span.rb +15 -0
  60. data/lib/kitchen/document.rb +142 -0
  61. data/lib/kitchen/element.rb +15 -0
  62. data/lib/kitchen/element_base.rb +444 -0
  63. data/lib/kitchen/element_enumerator.rb +12 -0
  64. data/lib/kitchen/element_enumerator_base.rb +101 -0
  65. data/lib/kitchen/element_enumerator_factory.rb +111 -0
  66. data/lib/kitchen/element_factory.rb +32 -0
  67. data/lib/kitchen/errors.rb +4 -0
  68. data/lib/kitchen/example_element.rb +20 -0
  69. data/lib/kitchen/example_element_enumerator.rb +13 -0
  70. data/lib/kitchen/figure_element.rb +20 -0
  71. data/lib/kitchen/figure_element_enumerator.rb +13 -0
  72. data/lib/kitchen/mixins/block_error_if.rb +19 -0
  73. data/lib/kitchen/note_element.rb +43 -0
  74. data/lib/kitchen/note_element_enumerator.rb +13 -0
  75. data/lib/kitchen/oven.rb +61 -0
  76. data/lib/kitchen/page_element.rb +51 -0
  77. data/lib/kitchen/page_element_enumerator.rb +13 -0
  78. data/lib/kitchen/pantry.rb +35 -0
  79. data/lib/kitchen/patches/nokogiri.rb +31 -0
  80. data/lib/kitchen/patches/renderable.rb +31 -0
  81. data/lib/kitchen/patches/string.rb +5 -0
  82. data/lib/kitchen/recipe.rb +78 -0
  83. data/lib/kitchen/search_history.rb +33 -0
  84. data/lib/kitchen/selectors/base.rb +8 -0
  85. data/lib/kitchen/selectors/standard_1.rb +12 -0
  86. data/lib/kitchen/table_element.rb +36 -0
  87. data/lib/kitchen/table_element_enumerator.rb +13 -0
  88. data/lib/kitchen/term_element.rb +16 -0
  89. data/lib/kitchen/term_element_enumerator.rb +13 -0
  90. data/lib/kitchen/transliterations.rb +19 -0
  91. data/lib/kitchen/type_casting_element_enumerator.rb +23 -0
  92. data/lib/kitchen/utils.rb +19 -0
  93. data/lib/kitchen/version.rb +3 -0
  94. data/lib/locales/en.yml +21 -0
  95. data/lib/notes.md +9 -0
  96. data/openstax_kitchen.gemspec +39 -0
  97. data/tutorials/00/expected_baked.html +3 -0
  98. data/tutorials/00/raw.html +3 -0
  99. data/tutorials/00/solution_1.rb +7 -0
  100. data/tutorials/00/solution_2.rb +6 -0
  101. data/tutorials/01/expected_baked.html +66 -0
  102. data/tutorials/01/raw.html +62 -0
  103. data/tutorials/01/solution_1.rb +16 -0
  104. data/tutorials/01/solution_2.rb +24 -0
  105. data/tutorials/02/expected_baked.html +207 -0
  106. data/tutorials/02/raw.html +201 -0
  107. data/tutorials/02/solution_1.rb +29 -0
  108. data/tutorials/03/expected_baked.html +33 -0
  109. data/tutorials/03/raw.html +31 -0
  110. data/tutorials/03/solution_1.rb +16 -0
  111. data/tutorials/03/solution_2.rb +15 -0
  112. data/tutorials/04/expected_baked.html +36 -0
  113. data/tutorials/04/raw.html +36 -0
  114. data/tutorials/04/solution_1.rb +20 -0
  115. data/tutorials/04/solution_2.rb +25 -0
  116. data/tutorials/05/expected_baked.html +11 -0
  117. data/tutorials/05/raw.html +11 -0
  118. data/tutorials/05/solution_1.rb +9 -0
  119. data/tutorials/check_it +64 -0
  120. data/tutorials/setup_my_recipes +30 -0
  121. metadata +278 -0
@@ -0,0 +1,35 @@
1
+ module Kitchen
2
+ class ChapterElement < ElementBase
3
+
4
+ def initialize(node:, document: nil)
5
+ super(node: node,
6
+ document: document,
7
+ enumerator_class: ChapterElementEnumerator,
8
+ short_type: :chapter)
9
+ end
10
+
11
+ def title
12
+ # Get the title in the immediate children, not the one in the metadata. Could use
13
+ # CSS of ":not([data-type='metadata']) > [data-type='document-title'], [data-type='document-title']"
14
+ # but xpath is shorter
15
+ first!("./*[@data-type = 'document-title']")
16
+ end
17
+
18
+ def introduction_page
19
+ pages('.introduction').first
20
+ end
21
+
22
+ def glossaries
23
+ search("div[data-type='glossary']")
24
+ end
25
+
26
+ def key_equations
27
+ search("section.key-equations")
28
+ end
29
+
30
+ def self.is_the_element_class_for?(node)
31
+ node['data-type'] == "chapter"
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ module Kitchen
2
+ class ChapterElementEnumerator < ElementEnumeratorBase
3
+
4
+ def self.factory
5
+ ElementEnumeratorFactory.new(
6
+ default_css_or_xpath: "div[data-type='chapter']", # TODO element.document.selectors.chapter
7
+ sub_element_class: ChapterElement,
8
+ enumerator_class: self
9
+ )
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ module Kitchen
2
+ # A place to store lists of things during recipe work. Essentially a
3
+ # slightly fancy array.
4
+ #
5
+ class Clipboard
6
+ include Enumerable
7
+
8
+ attr_reader :items
9
+
10
+ def initialize
11
+ clear
12
+ end
13
+
14
+ def add(item)
15
+ @items.push(item)
16
+ end
17
+
18
+ def clear
19
+ @items = []
20
+ end
21
+
22
+ def paste
23
+ @items.map(&:paste).join("")
24
+ end
25
+
26
+ def each(&block)
27
+ @items.each do |item|
28
+ block.call(item)
29
+ end
30
+ self
31
+ end
32
+
33
+ def sort_by!(&block)
34
+ @items.sort_by!(&block)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ module Kitchen
2
+ class CompositeChapterElement < ElementBase
3
+
4
+ def initialize(node:, document: nil)
5
+ super(node: node,
6
+ document: document,
7
+ enumerator_class: ElementEnumerator,
8
+ short_type: :composite_chapter)
9
+ end
10
+
11
+ def title
12
+ # Get the title in the immediate children, not the one in the metadata. Could use
13
+ # CSS of ":not([data-type='metadata']) > [data-type='document-title'], [data-type='document-title']"
14
+ # but xpath is shorter
15
+ first!("./*[@data-type = 'document-title']")
16
+ end
17
+
18
+ def self.is_the_element_class_for?(node)
19
+ node['data-type'] == "composite-chapter"
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ module Kitchen
2
+ class CompositePageElement < ElementBase
3
+
4
+ def initialize(node:, document: nil)
5
+ super(node: node,
6
+ document: document,
7
+ enumerator_class: CompositePageElementEnumerator,
8
+ short_type: :composite_page)
9
+ end
10
+
11
+ def title
12
+ # Get the title in the immediate children, not the one in the metadata. Could use
13
+ # CSS of ":not([data-type='metadata']) > [data-type='document-title'], [data-type='document-title']"
14
+ # but xpath is shorter
15
+ first!("./*[@data-type = 'document-title']")
16
+ end
17
+
18
+ def self.is_the_element_class_for?(node)
19
+ node['data-type'] == "composite-page"
20
+ end
21
+
22
+ def is_index?
23
+ has_class?("os-index-container")
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ module Kitchen
2
+ class CompositePageElementEnumerator < ElementEnumeratorBase
3
+
4
+ def self.factory
5
+ ElementEnumeratorFactory.new(
6
+ default_css_or_xpath: "div[data-type='composite-page']",
7
+ sub_element_class: CompositePageElement,
8
+ enumerator_class: self
9
+ )
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ module Kitchen
2
+ class Config
3
+
4
+ attr_reader :selectors
5
+
6
+ def self.new_from_file(file)
7
+ raise "NYI"
8
+ end
9
+
10
+ def self.new_default
11
+ new
12
+ end
13
+
14
+ def initialize(hash: {}, selectors: nil)
15
+ @selectors = selectors || Kitchen::Selectors::Standard1.new
16
+ @hash = hash
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,34 @@
1
+ module Kitchen
2
+ class Counter # hehe
3
+
4
+ def initialize
5
+ reset
6
+ end
7
+
8
+ # Increase the value of the counter
9
+ #
10
+ # @param by [Integer] the amount to increase by
11
+ #
12
+ def increment(by: 1)
13
+ @value += by
14
+ end
15
+
16
+ # (see #increment)
17
+ alias_method :inc, :increment
18
+
19
+ # Returns the value of the counter
20
+ #
21
+ # @return [Integer]
22
+ def get
23
+ @value
24
+ end
25
+
26
+ # Reset the value of the counter
27
+ #
28
+ # @param to [Integer] the value to reset to
29
+ def reset(to: 0)
30
+ @value = to
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,80 @@
1
+ require 'rainbow'
2
+
3
+ module Kitchen
4
+ module Debug
5
+
6
+ CONTEXT_LINES = 2
7
+
8
+ def self.print_recipe_error(error:, source_location:, document:)
9
+ error_location = error.backtrace.detect do |entry|
10
+ entry.start_with?(source_location) || entry.match?(/kitchen\/lib\/kitchen\/directions/)
11
+ end
12
+
13
+ error_filename, error_line_number = error_location.match(/(.*):(\d+):/)[1..2]
14
+ error_line_number = error_line_number.to_i
15
+
16
+ puts "The recipe has an error: " + Rainbow(error.message).bright
17
+ puts "at or near the following #{Rainbow('highlighted').red} line"
18
+ puts "\n"
19
+ puts "-----+ #{Rainbow(error_filename).bright} -----"
20
+
21
+ line_numbers_before = (error_line_number-CONTEXT_LINES..error_line_number-1)
22
+ line_numbers_after = (error_line_number+1..error_line_number+CONTEXT_LINES)
23
+
24
+ File.readlines(error_filename).each.with_index do |line, line_index|
25
+ line_number = line_index + 1
26
+
27
+ if line_numbers_before.include?(line_number) ||
28
+ line_numbers_after.include?(line_number)
29
+ print_file_line(line_number, line)
30
+ end
31
+
32
+ if line_number == error_line_number
33
+ print_file_line(line_number, Rainbow(line.chomp).red)
34
+ end
35
+ end
36
+
37
+ puts "\n"
38
+
39
+ print_specific_help_line(error)
40
+
41
+ current_node = document.location&.raw
42
+
43
+ if current_node
44
+ puts "Encountered on line #{Rainbow(current_node.line).red} in the input document on element:"
45
+ puts current_node.dup.tap{|node| node.inner_html="..." if node.inner_html != ""}.to_s
46
+ puts "\n"
47
+ end
48
+
49
+ if ENV['VERBOSE']
50
+ puts "Full backtrace:\n"
51
+ puts error.backtrace.map{|line| Rainbow(line).blue}
52
+ else
53
+ puts "Full backtrace suppressed (enable by setting the VERBOSE environment variable to something)"
54
+ end
55
+ end
56
+
57
+ protected
58
+
59
+ def self.print_file_line(line_number, line)
60
+ puts "#{"%5s" % line_number}| #{line}"
61
+ end
62
+
63
+ def self.print_specific_help_line(error)
64
+ # No specific help lines at the moment, an example is shown for the future
65
+
66
+ # help_line = case error.message
67
+ # when /some string in error/
68
+ # "`foo` needs to be called on a Bar object"
69
+ # else
70
+ # nil
71
+ # end
72
+
73
+ # if !help_line.nil?
74
+ # puts help_line
75
+ # puts "\n"
76
+ # end
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,26 @@
1
+ module Kitchen
2
+ module Directions
3
+ module BakeAppendix
4
+
5
+ def self.v1(page:, number:)
6
+ title = page.title
7
+ title.name = "h1"
8
+ title.replace_children(with:
9
+ <<~HTML
10
+ <span class="os-part-text">#{I18n.t(:appendix)} </span>
11
+ <span class="os-number">#{number}</span>
12
+ <span class="os-divider"> </span>
13
+ <span data-type="" itemprop="" class="os-text">#{title.children}</span>
14
+ HTML
15
+ )
16
+
17
+ # Make a section with data-depth of X have a header level of X+1
18
+ page.search("section").each do |section|
19
+ title = section.first!("[data-type='title']")
20
+ title.name = "h#{section['data-depth'].to_i + 1}"
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,34 @@
1
+ module Kitchen
2
+ module Directions
3
+ module BakeChapterGlossary
4
+
5
+ def self.v1(chapter:, metadata_source:)
6
+ metadata_elements = metadata_source.search(%w(.authors .publishers .print-style
7
+ .permissions [data-type='subject'])).copy
8
+
9
+ definitions = chapter.glossaries.search("dl").cut
10
+ definitions.sort_by! do |definition|
11
+ [definition.first("dt").text.downcase, definition.first("dd").text.downcase]
12
+ end
13
+
14
+ chapter.glossaries.trash
15
+
16
+ chapter.append(child:
17
+ <<~HTML
18
+ <div class="os-eoc os-glossary-container" data-type="composite-page" data-uuid-key="glossary">
19
+ <h2 data-type="document-title">
20
+ <span class="os-text">#{I18n.t(:eoc_key_terms_title)}</span>
21
+ </h2>
22
+ <div data-type="metadata" style="display: none;">
23
+ <h1 data-type="document-title" itemprop="name">#{I18n.t(:eoc_key_terms_title)}</h1>
24
+ #{metadata_elements.paste}
25
+ </div>
26
+ #{definitions.paste}
27
+ </div>
28
+ HTML
29
+ ) unless definitions.none?
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,58 @@
1
+ module Kitchen
2
+ module Directions
3
+ module BakeChapterIntroductions
4
+
5
+ def self.v1(book:)
6
+ book.chapters.each do |chapter|
7
+ outline_items_html = chapter.pages.map do |page|
8
+ next if page.is_introduction?
9
+
10
+ <<~HTML
11
+ <div class="os-chapter-objective">
12
+ <a class="os-chapter-objective" href="##{page.title[:id]}">
13
+ <span class="os-number">#{chapter.count_in(:book)}.#{page.count_in(:chapter)-1}</span>
14
+ <span class="os-divider"> </span>
15
+ <span data-type="" itemprop="" class="os-text">#{page.title.children[0].text}</span>
16
+ </a>
17
+ </div>
18
+ HTML
19
+ end.join("")
20
+
21
+ introduction_page = chapter.introduction_page
22
+
23
+ introduction_page.search("div[data-type='description']").trash
24
+ introduction_page.search("div[data-type='abstract']").trash
25
+
26
+ title = introduction_page.title.cut
27
+ title.name = "h2"
28
+ MoveTitleTextIntoSpan.v1(title: title)
29
+
30
+ intro_content = introduction_page.search("> :not([data-type='metadata']):not(figure)").cut
31
+
32
+ introduction_page.append(child:
33
+ <<~HTML
34
+ <div class="intro-body">
35
+ <div class="os-chapter-outline">
36
+ <h3 class="os-title">Chapter Outline</h3>
37
+ #{outline_items_html}
38
+ </div>
39
+ <div class="intro-text">
40
+ #{title.paste}
41
+ #{intro_content.paste}
42
+ </div>
43
+ </div>
44
+ HTML
45
+ )
46
+ end
47
+
48
+ v1_update_selectors(book)
49
+ end
50
+
51
+ def self.v1_update_selectors(something_with_selectors)
52
+ something_with_selectors.selectors.title_in_introduction_page =
53
+ ".intro-text > [data-type='document-title']"
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,30 @@
1
+ module Kitchen
2
+ module Directions
3
+ module BakeChapterKeyEquations
4
+
5
+ def self.v1(chapter:, metadata_source:)
6
+ metadata_elements = metadata_source.search(%w(.authors .publishers .print-style
7
+ .permissions [data-type='subject'])).copy
8
+
9
+ chapter.key_equations.search("h3").trash
10
+ key_equations = chapter.key_equations.cut
11
+
12
+ chapter.append(child:
13
+ <<~HTML
14
+ <div class="os-eoc os-key-equations-container" data-type="composite-page" data-uuid-key=".key-equations">
15
+ <h2 data-type="document-title">
16
+ <span class="os-text">#{I18n.t(:eoc_key_equations)}</span>
17
+ </h2>
18
+ <div data-type="metadata" style="display: none;">
19
+ <h1 data-type="document-title" itemprop="name">#{I18n.t(:eoc_key_equations)}</h1>
20
+ #{metadata_elements.paste}
21
+ </div>
22
+ #{key_equations.paste}
23
+ </div>
24
+ HTML
25
+ ) unless key_equations.none?
26
+ end
27
+
28
+ end
29
+ end
30
+ end