spirit 0.2 → 0.5

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 (40) hide show
  1. checksums.yaml +7 -0
  2. data/lib/spirit.rb +8 -10
  3. data/lib/spirit/constants.rb +17 -7
  4. data/lib/spirit/document.rb +4 -9
  5. data/lib/spirit/errors.rb +0 -1
  6. data/lib/spirit/logger.rb +5 -6
  7. data/lib/spirit/manifest.rb +4 -5
  8. data/lib/spirit/render.rb +0 -2
  9. data/lib/spirit/render/errors.rb +0 -4
  10. data/lib/spirit/render/html.rb +17 -117
  11. data/lib/spirit/render/processable.rb +78 -0
  12. data/lib/spirit/render/processors.rb +15 -0
  13. data/lib/spirit/render/processors/base.rb +40 -0
  14. data/lib/spirit/render/processors/block_image_processor.rb +49 -0
  15. data/lib/spirit/render/processors/headers_processor.rb +41 -0
  16. data/lib/spirit/render/processors/layout_processor.rb +28 -0
  17. data/lib/spirit/render/processors/math_processor.rb +102 -0
  18. data/lib/spirit/render/processors/problems_processor.rb +76 -0
  19. data/lib/spirit/render/processors/pygments_processor.rb +22 -0
  20. data/lib/spirit/render/processors/sanitize_processor.rb +86 -0
  21. data/lib/spirit/render/templates.rb +1 -3
  22. data/lib/spirit/render/templates/header.rb +2 -3
  23. data/lib/spirit/render/templates/image.rb +6 -13
  24. data/lib/spirit/render/templates/multi.rb +9 -10
  25. data/lib/spirit/render/templates/navigation.rb +4 -5
  26. data/lib/spirit/render/templates/problem.rb +24 -28
  27. data/lib/spirit/render/templates/short.rb +2 -3
  28. data/lib/spirit/render/templates/table.rb +2 -2
  29. data/lib/spirit/render/templates/template.rb +12 -8
  30. data/lib/spirit/version.rb +1 -2
  31. data/views/header.haml +1 -1
  32. data/views/img.haml +2 -2
  33. data/views/layout.haml +27 -0
  34. data/views/multi.haml +10 -14
  35. data/views/nav.haml +2 -2
  36. data/views/short.haml +6 -11
  37. data/views/table.haml +20 -26
  38. metadata +36 -57
  39. data/lib/spirit/render/sanitize.rb +0 -90
  40. data/views/exe.haml +0 -5
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4844986a578645c04c236ef9ebe931cbfaf7aab9
4
+ data.tar.gz: 280b2cfbb472f5670eb364e9954c3e42bd87ca5a
5
+ SHA512:
6
+ metadata.gz: 616c1c9e6fbf583efbded87e74baf193637cac7c582e80044165531d2a1beab46bf90530bbdf0e9013a1351a8f0c5e208dcc6c598208c82962a4b267804a57b7
7
+ data.tar.gz: 867670f2f3533580428c654c466b4d4d16d5e822710e13f98b253b63a865ae7f57c41fa24875af7f5ded5771bc856c16cf14454b76bcd7ae0d5f30398877bc4d
@@ -1,4 +1,4 @@
1
- # ~*~ encoding: utf-8 ~*~
1
+ require 'spirit/version'
2
2
  require 'spirit/logger'
3
3
  require 'spirit/constants'
4
4
  require 'spirit/errors'
@@ -6,17 +6,15 @@ require 'spirit/document'
6
6
  require 'spirit/manifest'
7
7
 
8
8
  module Spirit
9
- extend self
10
9
 
11
- attr_reader :logger
10
+ mattr_accessor :logger
11
+ @@logger = Logger.new '/dev/null'
12
12
 
13
- # Initializes the logger. It takes the same arguments as +Logger::new+.
14
- # Invoke this at the beginning if you wish {Spirit} to log to another
15
- # location than +STDOUT+.
16
- def initialize_logger(output=STDOUT, *args)
17
- @logger = Logger.new output, *args
13
+ # Invoke with args for {Logger} to enable logging.
14
+ def self.reset_logger(io=STDOUT, *args)
15
+ self.logger = Logger.new(io, *args)
16
+ logger.formatter = Logger::Formatter.new
17
+ logger.info "Spirit v#{VERSION}"
18
18
  end
19
19
 
20
- initialize_logger
21
-
22
20
  end
@@ -1,10 +1,9 @@
1
- # ~*~ encoding: utf-8 ~*~
2
1
  require 'tmpdir'
3
2
 
4
3
  module Spirit
5
4
 
6
5
  # Path to templates
7
- VIEWS = File.join File.dirname(__FILE__), *%w(.. .. views)
6
+ VIEWS = File.join(File.dirname(__FILE__), *%w(.. .. views)).freeze
8
7
 
9
8
  # Markdown extensions for Redcarpet
10
9
  MARKDOWN_EXTENSIONS = {
@@ -13,15 +12,26 @@ module Spirit
13
12
  fenced_code_blocks: true,
14
13
  autolink: true,
15
14
  strikethrough: true,
16
- }
15
+ }.freeze
17
16
 
18
- SOLUTION_DIR = Dir.tmpdir
19
- SOLUTION_EXT = '.sol'
17
+ # Renderer configuration options
18
+ RENDERER_CONFIG = {
19
+ hard_wrap: true,
20
+ no_styles: true,
21
+ }.freeze
22
+
23
+ HAML_CONFIG = {
24
+ escape_html: true,
25
+ format: :html5,
26
+ }.freeze
27
+
28
+ SOLUTION_DIR = Dir.tmpdir.freeze
29
+ SOLUTION_EXT = '.sol'.freeze
20
30
 
21
31
  # Name of index page.
22
- INDEX = 'index.md'
32
+ INDEX = 'index.md'.freeze
23
33
 
24
34
  # Name of manifest file.
25
- MANIFEST = 'manifest.yml'
35
+ MANIFEST = 'manifest.yml'.freeze
26
36
 
27
37
  end
@@ -1,4 +1,4 @@
1
- # ~*~ encoding: utf-8 ~*~
1
+ require 'active_support/core_ext/module/delegation'
2
2
  require 'spirit/constants'
3
3
  require 'spirit/errors'
4
4
  require 'spirit/render'
@@ -6,10 +6,10 @@ require 'spirit/render'
6
6
  module Spirit
7
7
 
8
8
  # Document written in Genie Markup Language.
9
- # @todo TODO clean?
10
9
  class Document
11
10
 
12
- attr_accessor :data, :engine
11
+ attr_reader :data, :engine, :rndr
12
+ delegate :solutions, to: :rndr
13
13
 
14
14
  # Creates a new document from the given source. It should contain valid
15
15
  # markdown + embedded YAML.
@@ -17,7 +17,7 @@ module Spirit
17
17
  # @param [Hash] opts options for {::Redcarpet}
18
18
  def initialize(source, opts={})
19
19
  opts = MARKDOWN_EXTENSIONS.merge opts
20
- rndr = Render::HTML.new(name: opts[:name])
20
+ @rndr = Render::HTML.new
21
21
  @engine = ::Redcarpet::Markdown.new(rndr, opts)
22
22
  @data = case
23
23
  when source.respond_to?(:to_str) then source.to_str
@@ -25,11 +25,6 @@ module Spirit
25
25
  else nil end
26
26
  end
27
27
 
28
- # @return [Boolean] true iff if was a clean parse with no errors.
29
- def clean?
30
- # TODO
31
- end
32
-
33
28
  # Rendered output is returned as a string if +anIO+ is not provided. The
34
29
  # output is sanitized with {Spirit::Render::Sanitize}, and should be
35
30
  # considered safe for embedding into a HTML page without further escaping or
@@ -1,4 +1,3 @@
1
- # ~*~ encoding: utf-8 ~*~
2
1
  module Spirit
3
2
 
4
3
  # The base exception for all {Spirit} errors. Raised if the parser is unable
@@ -1,4 +1,3 @@
1
- # ~*~ encoding: utf-8 ~*~
2
1
  require 'active_support/core_ext/logger'
3
2
 
4
3
  module Spirit
@@ -6,13 +5,13 @@ module Spirit
6
5
  # @see https://github.com/chriseppstein/compass/blob/stable/lib/compass/logger.rb
7
6
  class Logger < ::Logger
8
7
 
9
- COLORS = { clear: 0, red: 31, green: 32, blue: 35, yellow: 33, grey: 37 }
8
+ COLORS = { clear: 0, red: 31, green: 32, blue: 35, yellow: 33, grey: 37 }.freeze
10
9
 
11
10
  ACTION_COLORS = {
12
- :error => :red,
13
- :warning => :yellow,
14
- :problem => :blue,
15
- }
11
+ error: :red,
12
+ warning: :yellow,
13
+ problem: :blue,
14
+ }.freeze
16
15
 
17
16
  # Record that an action has occurred.
18
17
  def record(action, *args)
@@ -1,4 +1,3 @@
1
- # ~*~ encoding: utf-8 ~*~
2
1
  require 'active_support/core_ext/hash'
3
2
  require 'active_support/core_ext/string'
4
3
  require 'yaml'
@@ -14,11 +13,11 @@ module Spirit
14
13
  verify: { 'bin' => 'string', 'arg_prefix' => 'string' },
15
14
  title: 'string',
16
15
  description: 'string',
17
- categories: %w(string),
18
- static_paths: %w(string)
19
- }
16
+ categories: %w[string array],
17
+ static_paths: %w[string array]
18
+ }.freeze
20
19
 
21
- # Creates a new configuration hash from the given source.
20
+ # Creates a new manifest from the given source.
22
21
  def initialize(hash)
23
22
  super nil
24
23
  hash ||= {}
@@ -1,5 +1,3 @@
1
- # ~*~ encoding: utf-8 ~*~
2
- require 'albino'
3
1
  require 'haml'
4
2
  require 'redcarpet'
5
3
 
@@ -1,11 +1,7 @@
1
1
  # ~*~ encoding: utf-8 ~*~
2
2
  module Spirit
3
-
4
3
  module Render
5
-
6
4
  # The base exception for {Spirit::Render} errors.
7
5
  class RenderError < DocumentError; end
8
-
9
6
  end
10
-
11
7
  end
@@ -1,10 +1,11 @@
1
- # ~*~ encoding: utf-8 ~*~
1
+ require 'active_support/core_ext/module/delegation'
2
+
2
3
  require 'spirit/render/errors'
3
- require 'spirit/render/sanitize'
4
4
  require 'spirit/render/templates'
5
+ require 'spirit/render/processable'
6
+ require 'spirit/render/processors'
5
7
 
6
8
  module Spirit
7
-
8
9
  module Render
9
10
 
10
11
  # HTML Renderer for Genie Markup Language, which is just GitHub Flavored
@@ -13,124 +14,23 @@ module Spirit
13
14
  # @see Spirit::Tilt::Template
14
15
  # @see http://github.github.com/github-flavored-markdown/
15
16
  class HTML < ::Redcarpet::Render::HTML
17
+ include Processable
16
18
 
17
- @sanitize = Sanitize.new
18
- class << self; attr_reader :sanitize end
19
-
20
- # Paragraphs that start and end with '---' are treated as embedded YAML
21
- # and are parsed for questions/answers.
22
- PROBLEM_REGEX = /^"""$(.*?)^"""$/m
19
+ delegate :solutions, to: :problems
20
+ attr_accessor :navigation, :problems, :nesting
23
21
 
24
- # Paragraphs that only contain images are rendered with {Spirit::Render::Image}.
25
- IMAGE_REGEX = /\A\s*<img[^<>]+>\s*\z/m
26
-
27
- # Renderer configuration options.
28
- CONFIGURATION = {
29
- hard_wrap: true,
30
- no_styles: true,
31
- }
22
+ use Processors::SanitizeProcessor
23
+ use Processors::MathProcessor
24
+ use Processors::LayoutProcessor
25
+ use Processors::ProblemsProcessor
26
+ use Processors::PygmentsProcessor
27
+ use Processors::BlockImageProcessor
28
+ use Processors::HeadersProcessor
32
29
 
33
30
  # Creates a new HTML renderer.
34
- # @param [Hash] options described in the RedCarpet documentation.
35
- def initialize(options={})
36
- super CONFIGURATION.merge options
37
- @nav, @headers = Navigation.new, Headers.new
38
- @prob, @img = 0, 0 # indices for Problem #, Figure #
39
- @name = options.delete(:name) || 'untitled'
40
- end
41
-
42
- # Pygmentizes code blocks.
43
- # @param [String] code code block contents
44
- # @param [String] marker name of language, for syntax highlighting
45
- # @return [String] highlighted code
46
- def block_code(code, marker)
47
- #language, type, id = (marker || 'text').split ':'
48
- #highlighted = Albino.colorize code, language
49
- language, _, _ = (marker || 'text').split ':'
50
- Albino.colorize code, language
51
- # TODO
52
- #case type
53
- #when 'demo', 'test'
54
- # executable id: id, raw: code, colored: highlighted
55
- #else highlighted end
56
- end
57
-
58
- # Detects block images and renders them as such.
59
- # @return [String] rendered html
60
- def paragraph(text)
61
- case text
62
- when IMAGE_REGEX then block_image(text)
63
- else p(text) end
64
- rescue RenderError => e # fall back to paragraph
65
- Spirit.logger.warn e.message
66
- p(text)
67
- end
68
-
69
- # Increases all header levels by one and keeps a navigation bar.
70
- # @return [String] rendered html
71
- def header(text, level)
72
- html, name = h(text, level += 1)
73
- @nav.append(text, name) if level == 2
74
- html
75
- end
76
-
77
- # Runs a first pass through the document to look for problem blocks.
78
- # @param [String] document markdown document
79
- def preprocess(document)
80
- document.gsub(PROBLEM_REGEX) { |yaml| problem $1 }
81
- end
82
-
83
- # Sanitizes the final document.
84
- # @param [String] document html document
85
- # @return [String] sanitized document
86
- def postprocess(document)
87
- HTML.sanitize.clean(@nav.render + document.force_encoding('utf-8'))
88
- end
89
-
90
- private
91
-
92
- # Prepares an executable code block.
93
- # @option opts [String] id author-supplied ID
94
- # @option opts [String] raw code to execute
95
- # @option opts [String] colored syntax highlighted code
96
- # @return [String]
97
- #def executable(opts)
98
- # opts[:colored] + @exe.render(Object.new, id: opts[:id], raw: opts[:raw])
99
- #end
100
-
101
- # Prepares a problem form. Returns +yaml+ if the given text does not
102
- # contain valid yaml markup for a problem.
103
- # @param [String] yaml YAML markup
104
- # @return [String] rendered HTML
105
- def problem(yaml)
106
- problem = Problem.parse(yaml)
107
- Spirit.logger.record :problem, "ID: #{problem.id}"
108
- problem.save!(@name) and problem.render(index: @prob += 1)
109
- rescue RenderError
110
- yaml
111
- end
112
-
113
- # Prepares a block image. Raises {RenderError} if the given text does not
114
- # contain a valid image block.
115
- # @param [String] text markdown text
116
- # @return [String] rendered HTML
117
- def block_image(text)
118
- Image.parse(text).render(index: @img += 1)
119
- end
120
-
121
- # Wraps the given text with header tags.
122
- # @return [String] rendered HTML
123
- # @return [String] anchor name
124
- def h(text, level)
125
- header = @headers.add(text, level)
126
- return header.render, header.name
127
- end
128
-
129
- # Wraps the given text with paragraph tags.
130
- # @param [String] text paragraph text
131
- # @return [String] rendered html
132
- def p(text)
133
- '<p>' + text + '</p>'
31
+ # @param [Hash] opts described in the RedCarpet documentation
32
+ def initialize(opts={})
33
+ super RENDERER_CONFIG.merge opts
134
34
  end
135
35
 
136
36
  end
@@ -0,0 +1,78 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+ require 'active_support/concern'
3
+
4
+ module Spirit
5
+ module Render
6
+
7
+ # Provides methods to attach processors.
8
+ module Processable
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ class_attribute :processors,
13
+ instance_reader: false, instance_writer: false
14
+ self.processors = {} # array of processor classes
15
+ attr_accessor :processors # array of processor instances
16
+ end
17
+
18
+ module ClassMethods
19
+
20
+ # @example using a processor
21
+ # include Processable
22
+ # use Processor::MathProcessor
23
+ def use(processor)
24
+ processor.events.each do |event|
25
+ processors[event] ||= []
26
+ processors[event] << processor
27
+ define event unless method_defined? event
28
+ end
29
+ end
30
+
31
+ def define(event)
32
+ define_method(event) { |*args| invoke_callbacks event, *args }
33
+ end
34
+
35
+ end
36
+
37
+ def preprocess(document)
38
+ instantiate_processors document
39
+ invoke_callbacks :preprocess, document
40
+ end
41
+
42
+ def postprocess(document)
43
+ invoke_callbacks_in_reverse :postprocess, document
44
+ end
45
+
46
+ # Instantiate processors. {#processors} becomes a hash from classes to
47
+ # instances.
48
+ def instantiate_processors(document)
49
+ processors = self.class.processors.values.flatten.uniq
50
+ instances = processors.map { |c| [ c, c.new(self, document) ] }
51
+ self.processors = Hash[instances]
52
+ end
53
+ private :instantiate_processors
54
+
55
+ def invoke_callbacks(event, *args)
56
+ processors = processors_for event
57
+ processors.each { |p| args = p.invoke_callbacks_for event, *args }
58
+ args
59
+ end
60
+ private :invoke_callbacks
61
+
62
+ def invoke_callbacks_in_reverse(event, *args)
63
+ processors = processors_for(event).reverse
64
+ processors.each { |p| args = p.invoke_callbacks_for event, *args }
65
+ args
66
+ end
67
+ private :invoke_callbacks_in_reverse
68
+
69
+ def processors_for(event)
70
+ classes = self.class.processors[event] || []
71
+ classes.map { |c| processors[c] }.compact
72
+ end
73
+ private :processors_for
74
+
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,15 @@
1
+ require 'spirit/render/processors/base'
2
+ require 'spirit/render/processors/math_processor'
3
+ require 'spirit/render/processors/sanitize_processor'
4
+ require 'spirit/render/processors/layout_processor'
5
+ require 'spirit/render/processors/problems_processor'
6
+ require 'spirit/render/processors/pygments_processor'
7
+ require 'spirit/render/processors/block_image_processor'
8
+ require 'spirit/render/processors/headers_processor'
9
+
10
+ module Spirit
11
+ module Render
12
+ module Processors
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,40 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+ require 'active_support/core_ext/module/delegation'
3
+ require 'spirit/constants'
4
+
5
+ module Spirit
6
+ module Render
7
+ module Processors
8
+
9
+ class Base
10
+
11
+ class_attribute :hooks
12
+
13
+ def self.inherited(subclass)
14
+ subclass.hooks = {}
15
+ end
16
+
17
+ def self.process(event, callback)
18
+ hooks[event] ||= []
19
+ hooks[event] << callback
20
+ end
21
+
22
+ def self.events
23
+ hooks.keys
24
+ end
25
+
26
+ def initialize(*args)
27
+ end
28
+
29
+ def invoke_callbacks_for(event, *args)
30
+ hooks[event].each do |h|
31
+ args = public_send(h, *args)
32
+ end
33
+ args
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
40
+ end