class OpenStax::Content::FragmentSplitter
Attributes
processing_instructions[R]
reference_view_url[R]
Public Class Methods
new(processing_instructions, reference_view_url)
click to toggle source
# File lib/openstax/content/fragment_splitter.rb, line 8 def initialize(processing_instructions, reference_view_url) @processing_instructions = processing_instructions.map do |processing_instruction| OpenStruct.new(processing_instruction.to_h).tap do |pi_struct| pi_struct.fragments = [pi_struct.fragments].flatten.map do |fragment| fragment.to_s.split('_').map(&:capitalize).join end unless pi_struct.fragments.nil? pi_struct.only = [pi_struct.only].flatten.map(&:to_s) unless pi_struct.only.nil? pi_struct.except = [pi_struct.except].flatten.map(&:to_s) unless pi_struct.except.nil? end end @reference_view_url = reference_view_url end
Public Instance Methods
split_into_fragments(root, type = nil)
click to toggle source
Splits the given root node into fragments according to the processing instructions
# File lib/openstax/content/fragment_splitter.rb, line 23 def split_into_fragments(root, type = nil) result = [root.dup] type_string = type.to_s pis = processing_instructions.reject do |processing_instruction| processing_instruction.css.nil? || processing_instruction.css.empty? || processing_instruction.fragments.nil? || processing_instruction.fragments == ['Node'] || (!processing_instruction.only.nil? && !processing_instruction.only.include?(type_string)) || (!processing_instruction.except.nil? && processing_instruction.except.include?(type_string)) end @media_nodes = [] pis.each { |processing_instruction| result = process_array(result, processing_instruction) } # Flatten, remove empty nodes and transform remaining nodes into reading fragments result.map do |obj| next obj unless obj.is_a?(Nokogiri::XML::Node) fragment = OpenStax::Content::Fragment::Reading.new( node: obj, reference_view_url: reference_view_url ) fragment unless fragment.blank? end.compact.tap do |result| @media_nodes.each do |node| # Media processing instructions node.css('[id], [name]', custom_css).each do |linkable| css_array = [] css_array << "[href$=\"##{linkable[:id]}\"]" unless linkable[:id].nil? css_array << "[href$=\"##{linkable[:name]}\"]" unless linkable[:name].nil? css = css_array.join(', ') result.select(&:html?) .select { |fragment| fragment.has_css? css, custom_css } .each { |fragment| fragment.append node.dup } end end result.select(&:html?).each(&:transform_links!) end end
Protected Instance Methods
custom_css()
click to toggle source
# File lib/openstax/content/fragment_splitter.rb, line 69 def custom_css OpenStax::Content::CustomCss.instance end
get_fragment_instance(fragment_name, node, labels)
click to toggle source
Returns an instance of the given fragment class
# File lib/openstax/content/fragment_splitter.rb, line 74 def get_fragment_instance(fragment_name, node, labels) fragment_class = OpenStax::Content::Fragment.const_get fragment_name args = { node: node, labels: labels } args[:reference_view_url] = reference_view_url \ if fragment_class.is_a? OpenStax::Content::Fragment::Reading fragment = fragment_class.new args fragment unless fragment.blank? end
process_array(array, processing_instruction)
click to toggle source
Recursively process an array of Nodes and Fragments
# File lib/openstax/content/fragment_splitter.rb, line 183 def process_array(array, processing_instruction) array.flat_map do |obj| case obj when Array process_array(obj, processing_instruction) when Nokogiri::XML::Node process_node(obj, processing_instruction) else obj end end end
process_node(root, processing_instruction)
click to toggle source
Process a single Nokogiri::XML::Node
# File lib/openstax/content/fragment_splitter.rb, line 116 def process_node(root, processing_instruction) # Find first match node = root.at_css(processing_instruction.css, custom_css) # Base case return [ root ] if node.nil? num_fragments = processing_instruction.fragments.size if num_fragments == 0 # No splitting needed # Remove the match node and any empty parents from the tree recursive_compact(node, root) # Repeat the processing until no more matches process_node(root, processing_instruction) else compact_before = true compact_after = true # Check for special fragment cases (node) fragments = [] processing_instruction.fragments.each_with_index do |fragment, index| if fragment == 'Node' if index == 0 # fragments: [node, anything] - Don't remove node from root before fragments compact_before = false elsif index == num_fragments - 1 # fragments: [anything, node] - Don't remove node from root after fragments compact_after = false else # General case # Make a copy of the current node (up to the root), but remove all other nodes root_copy = root.dup node_copy = root_copy.at_css(processing_instruction.css, custom_css) remove_before(node_copy, root_copy) remove_after(node_copy, root_copy) fragments << root_copy end elsif fragment == 'Media' @media_nodes << node else fragments << get_fragment_instance(fragment, node, processing_instruction.labels) end end # Need to split the node tree # Copy the node content and find the same match in the copy root_copy = root.dup node_copy = root_copy.at_css(processing_instruction.css, custom_css) # One copy retains the content before the match; # the other retains the content after the match remove_after(node, root) remove_before(node_copy, root_copy) # Remove the match, its copy and any empty parents from the 2 trees recursive_compact(node, root) if compact_before recursive_compact(node_copy, root_copy) if compact_after # Repeat the processing until no more matches [ root ] + fragments + process_node(root_copy, processing_instruction) end end
recursive_compact(node, root)
click to toggle source
Recursively removes a node and its empty parents
# File lib/openstax/content/fragment_splitter.rb, line 84 def recursive_compact(node, root) return if node == root parent = node.parent node.remove recursive_compact(parent, root) if parent && (parent.content.nil? || parent.content.empty?) end
remove_after(node, root)
click to toggle source
Recursively removes all siblings after a node and its parents
# File lib/openstax/content/fragment_splitter.rb, line 105 def remove_after(node, root) return if node == root parent = node.parent siblings = parent.children index = siblings.index(node) parent.children = siblings.slice(0..index) remove_after(parent, root) end
remove_before(node, root)
click to toggle source
Recursively removes all siblings before a node and its parents
# File lib/openstax/content/fragment_splitter.rb, line 94 def remove_before(node, root) return if node == root parent = node.parent siblings = parent.children index = siblings.index(node) parent.children = siblings.slice(index..-1) remove_before(parent, root) end