jimjh/genie-parser

View on GitHub
lib/spirit/render/processors/problems_processor.rb

Summary

Maintainability
A
0 mins
Test Coverage
module Spirit
  module Render
    module Processors

      # Pre-processes problem markup in YAML.
      # Dependent on renderer#problems and renderer#nesting.
      class ProblemsProcessor < Base

        # Paragraphs that start and end with +"""+ are treated as embedded YAML
        # and are parsed for questions/answers.
        REGEX = /^"""$(.*?)^"""$/m

        MARKER = /\A<!-- %%(\d+)%% -->\z/

        attr_reader :problems, :solutions
        delegate :size, :count, :each, :each_with_index, to: :problems
        process  :preprocess, :filter
        process  :block_html, :replace

        def initialize(renderer, *args)
          @renderer  = renderer
          @problems  = []
          @solutions = []
          renderer.problems = self
        end

        # Replaces YAML markup in document with <!-- %%index%% -->
        # @return [String] document
        def filter(document)
          document.gsub(REGEX) { problem $1 }
        end

        def replace(html)
          return html unless is_marker? html
          replace_nesting html, renderer.nesting
          ''
        end

        private

        attr_reader :renderer

        # Update associated problem with nesting information.
        # @return [void]
        def replace_nesting(html, nesting)
          match = html.strip.match MARKER
          prob  = problems[match[1].to_i]
          prob.nesting = nesting.dup
        end

        # @return [Boolean] true iff the given html corresponds to a problem
        #   marker
        def is_marker?(html)
          html.strip =~ MARKER and problems[$1.to_i]
        end

        # If the given text contains valid YAML, returns a marker. Otherwise,
        # returns the original text.
        # @param  [String] text            candidate YAML markup
        # @return [String] text or marker
        def problem(text)
          p = Problem.parse(text)
          p.id = problems.size
          self.problems  << p
          self.solutions << {digest: p.digest, solution: Marshal.dump(p.answer)}
          Spirit.logger.record :problem, "ID: #{p.id}"
        rescue RenderError
          text
        else "<!-- %%#{p.id}%% -->\n\n<a data-pagination-destination='#{p.id + 1}'/>"
        end

      end

    end
  end
end