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.
- checksums.yaml +7 -0
- data/lib/spirit.rb +8 -10
- data/lib/spirit/constants.rb +17 -7
- data/lib/spirit/document.rb +4 -9
- data/lib/spirit/errors.rb +0 -1
- data/lib/spirit/logger.rb +5 -6
- data/lib/spirit/manifest.rb +4 -5
- data/lib/spirit/render.rb +0 -2
- data/lib/spirit/render/errors.rb +0 -4
- data/lib/spirit/render/html.rb +17 -117
- data/lib/spirit/render/processable.rb +78 -0
- data/lib/spirit/render/processors.rb +15 -0
- data/lib/spirit/render/processors/base.rb +40 -0
- data/lib/spirit/render/processors/block_image_processor.rb +49 -0
- data/lib/spirit/render/processors/headers_processor.rb +41 -0
- data/lib/spirit/render/processors/layout_processor.rb +28 -0
- data/lib/spirit/render/processors/math_processor.rb +102 -0
- data/lib/spirit/render/processors/problems_processor.rb +76 -0
- data/lib/spirit/render/processors/pygments_processor.rb +22 -0
- data/lib/spirit/render/processors/sanitize_processor.rb +86 -0
- data/lib/spirit/render/templates.rb +1 -3
- data/lib/spirit/render/templates/header.rb +2 -3
- data/lib/spirit/render/templates/image.rb +6 -13
- data/lib/spirit/render/templates/multi.rb +9 -10
- data/lib/spirit/render/templates/navigation.rb +4 -5
- data/lib/spirit/render/templates/problem.rb +24 -28
- data/lib/spirit/render/templates/short.rb +2 -3
- data/lib/spirit/render/templates/table.rb +2 -2
- data/lib/spirit/render/templates/template.rb +12 -8
- data/lib/spirit/version.rb +1 -2
- data/views/header.haml +1 -1
- data/views/img.haml +2 -2
- data/views/layout.haml +27 -0
- data/views/multi.haml +10 -14
- data/views/nav.haml +2 -2
- data/views/short.haml +6 -11
- data/views/table.haml +20 -26
- metadata +36 -57
- data/lib/spirit/render/sanitize.rb +0 -90
- data/views/exe.haml +0 -5
checksums.yaml
ADDED
@@ -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
|
data/lib/spirit.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
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
|
-
|
10
|
+
mattr_accessor :logger
|
11
|
+
@@logger = Logger.new '/dev/null'
|
12
12
|
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
data/lib/spirit/constants.rb
CHANGED
@@ -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
|
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
|
-
|
19
|
-
|
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
|
data/lib/spirit/document.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
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
|
-
|
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
|
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
|
data/lib/spirit/errors.rb
CHANGED
data/lib/spirit/logger.rb
CHANGED
@@ -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
|
-
|
13
|
-
:
|
14
|
-
:
|
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)
|
data/lib/spirit/manifest.rb
CHANGED
@@ -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
|
18
|
-
static_paths: %w
|
19
|
-
}
|
16
|
+
categories: %w[string array],
|
17
|
+
static_paths: %w[string array]
|
18
|
+
}.freeze
|
20
19
|
|
21
|
-
# Creates a new
|
20
|
+
# Creates a new manifest from the given source.
|
22
21
|
def initialize(hash)
|
23
22
|
super nil
|
24
23
|
hash ||= {}
|
data/lib/spirit/render.rb
CHANGED
data/lib/spirit/render/errors.rb
CHANGED
data/lib/spirit/render/html.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
|
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
|
-
|
18
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
35
|
-
def initialize(
|
36
|
-
super
|
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
|