spirit 0.2 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
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