faqml 0.1.1 → 0.1.2
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.
- data/Gemfile.lock +20 -0
- data/faqml.gemspec +9 -4
- data/lib/faqml.rb +6 -61
- data/lib/fml/engine.rb +31 -0
- data/lib/fml/parser.rb +148 -0
- data/lib/fml/template.rb +18 -0
- data/lib/fml/version.rb +4 -0
- data/test/test1.fml +14 -0
- metadata +41 -3
data/Gemfile.lock
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
faqml (0.1.2)
|
5
|
+
redcarpet (>= 2.1.1)
|
6
|
+
temple (>= 0.4.0)
|
7
|
+
tilt (~> 1.3.3)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
redcarpet (2.1.1)
|
13
|
+
temple (0.4.0)
|
14
|
+
tilt (1.3.3)
|
15
|
+
|
16
|
+
PLATFORMS
|
17
|
+
ruby
|
18
|
+
|
19
|
+
DEPENDENCIES
|
20
|
+
faqml!
|
data/faqml.gemspec
CHANGED
@@ -1,15 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.dirname(__FILE__) + '/lib/fml/version'
|
3
|
+
require 'date'
|
4
|
+
|
1
5
|
Gem::Specification.new do |s|
|
2
6
|
s.name = 'faqml'
|
3
|
-
s.version =
|
4
|
-
s.date =
|
7
|
+
s.version = FAQML::VERSION
|
8
|
+
s.date = Date.today.to_s
|
5
9
|
s.summary = "FAQ Markup Language"
|
6
|
-
s.description = "A Simple FAQ Markup Language
|
10
|
+
s.description = "A Simple FAQ Markup Language"
|
7
11
|
s.authors = ["Eric Redmond"]
|
8
12
|
s.email = 'eric@basho.com'
|
9
|
-
s.files = ["lib/faqml.rb"]
|
10
13
|
s.homepage = 'http://rubygems.org/gems/faqml'
|
11
14
|
|
12
15
|
s.add_dependency('redcarpet', '>= 2.1.1')
|
16
|
+
s.add_dependency('temple', '>= 0.4.0')
|
17
|
+
s.add_dependency('tilt', '~> 1.3.3')
|
13
18
|
|
14
19
|
s.files = `git ls-files`.split("\n")
|
15
20
|
s.require_paths = ["lib"]
|
data/lib/faqml.rb
CHANGED
@@ -1,63 +1,8 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
# FAQ Markup Language
|
2
|
-
# require 'rubygems'
|
3
|
-
require 'redcarpet'
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def to_html(attrs={})
|
11
|
-
linked_question = attrs[:linked_question] || true
|
12
|
-
|
13
|
-
faqs, current = [], :t
|
14
|
-
faqs << {:q => "", :a => "", :t => ""}
|
15
|
-
@data.split(/\n/m).each do |line|
|
16
|
-
# start an answer block
|
17
|
-
if line =~ /^===\s*$/
|
18
|
-
raise "Cannot begin an answer without a question title" if current != :q
|
19
|
-
current = :a
|
20
|
-
next
|
21
|
-
end
|
22
|
-
|
23
|
-
# start a new question block
|
24
|
-
if line =~ /^---/
|
25
|
-
line = line.sub(/^#/, '').strip
|
26
|
-
faqs << {:q => "", :a => "", :t => ""}
|
27
|
-
current = :t
|
28
|
-
next
|
29
|
-
end
|
30
|
-
|
31
|
-
faqs.last[current] += line
|
32
|
-
|
33
|
-
current = :q if current == :t && line.strip != ''
|
34
|
-
end
|
35
|
-
|
36
|
-
renderer = Redcarpet::Render::HTML.new
|
37
|
-
extensions = {fenced_code_blocks: true}
|
38
|
-
redcarpet = Redcarpet::Markdown.new(renderer, extensions)
|
39
|
-
|
40
|
-
buffer = ""
|
41
|
-
for faq in faqs
|
42
|
-
next if faq[:t] == ''
|
43
|
-
buffer += "<section class=\"qna\">\n"
|
44
|
-
if linked_question
|
45
|
-
buffer += "<h1><a href=\"#\">#{faq[:t].to_s.strip}</a></h1>\n"
|
46
|
-
else
|
47
|
-
buffer += "<h1>#{faq[:t].to_s.strip}</h1>\n"
|
48
|
-
end
|
49
|
-
buffer += "<div class=\"qna\">"
|
50
|
-
if faq[:q].to_s != ''
|
51
|
-
buffer += "<div class=\"question\">"
|
52
|
-
buffer += redcarpet.render(faq[:q])
|
53
|
-
buffer += "</div>"
|
54
|
-
end
|
55
|
-
buffer += "<div class=\"answer\">"
|
56
|
-
buffer += redcarpet.render(faq[:a].to_s)
|
57
|
-
buffer += "</div>"
|
58
|
-
buffer += "</div>"
|
59
|
-
buffer += "</section>\n"
|
60
|
-
end
|
61
|
-
return buffer
|
62
|
-
end
|
63
|
-
end
|
4
|
+
require 'temple'
|
5
|
+
require 'fml/version'
|
6
|
+
require 'fml/parser'
|
7
|
+
require 'fml/engine'
|
8
|
+
require 'fml/template'
|
data/lib/fml/engine.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
class FAQML::Engine < Temple::Engine
|
2
|
+
|
3
|
+
set_default_options :pretty => false,
|
4
|
+
:sort_attrs => true,
|
5
|
+
:attr_wrapper => '"',
|
6
|
+
:attr_delimiter => {'class' => ' '},
|
7
|
+
:remove_empty_attrs => true,
|
8
|
+
:generator => Temple::Generators::ArrayBuffer
|
9
|
+
# :default_tag => 'div'
|
10
|
+
|
11
|
+
use FAQML::Parser, :strict
|
12
|
+
|
13
|
+
# TODO: replace inline generation with a markdown filter
|
14
|
+
# use FAQML::MarkdownFilter
|
15
|
+
# use FAQML::WrapFilter
|
16
|
+
|
17
|
+
html :AttributeMerger, :attr_delimiter
|
18
|
+
html :AttributeSorter, :sort_attrs
|
19
|
+
html :AttributeRemover, :remove_empty_attrs
|
20
|
+
html :Pretty, :format, :attr_wrapper, :pretty, :indent
|
21
|
+
|
22
|
+
filter :Escapable, :use_html_safe, :disable_escape
|
23
|
+
filter :MultiFlattener
|
24
|
+
# filter :StaticMerger
|
25
|
+
# filter :DynamicInliner
|
26
|
+
|
27
|
+
# generator :ArrayBuffer, :buffer
|
28
|
+
|
29
|
+
use(:Optimizer) { (options[:streaming] ? Temple::Filters::StaticMerger : Temple::Filters::DynamicInliner).new }
|
30
|
+
use(:Generator) { options[:generator].new(options) }
|
31
|
+
end
|
data/lib/fml/parser.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'redcarpet'
|
2
|
+
|
3
|
+
# Transforms FML into a Temple expression
|
4
|
+
# @api private
|
5
|
+
class FAQML::Parser
|
6
|
+
include Temple::Mixins::Options
|
7
|
+
|
8
|
+
set_default_options :tabsize => 4,
|
9
|
+
:encoding => 'utf-8'
|
10
|
+
|
11
|
+
class SyntaxError < StandardError
|
12
|
+
attr_reader :error, :file, :line, :lineno, :column
|
13
|
+
|
14
|
+
def initialize(error, file, line, lineno, column)
|
15
|
+
@error = error
|
16
|
+
@file = file || '(__TEMPLATE__)'
|
17
|
+
@line = line.to_s
|
18
|
+
@lineno = lineno
|
19
|
+
@column = column
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
line = @line.strip
|
24
|
+
column = @column + line.size - @line.size
|
25
|
+
%{#{error}
|
26
|
+
#{file}, Line #{lineno}
|
27
|
+
#{line}
|
28
|
+
#{' ' * column}^
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(options = {})
|
34
|
+
super
|
35
|
+
|
36
|
+
renderer = Redcarpet::Render::HTML.new
|
37
|
+
extensions = {fenced_code_blocks: true}
|
38
|
+
@markdown = Redcarpet::Markdown.new(renderer, extensions)
|
39
|
+
|
40
|
+
@tab = ' ' * @options[:tabsize]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Compile string to Temple expression
|
44
|
+
#
|
45
|
+
# @param [String] str FML code
|
46
|
+
# @return [Array] Temple expression representing the code]]
|
47
|
+
def call(str)
|
48
|
+
# Set string encoding if option is set
|
49
|
+
if options[:encoding] && str.respond_to?(:encoding)
|
50
|
+
old_enc = str.encoding
|
51
|
+
str = str.dup if str.frozen?
|
52
|
+
str.force_encoding(options[:encoding])
|
53
|
+
# Fall back to old encoding if new encoding is invalid
|
54
|
+
str.force_encoding(old_enc) unless str.valid_encoding?
|
55
|
+
end
|
56
|
+
|
57
|
+
result = [:multi]
|
58
|
+
reset(str.split(/\r?\n/), result)
|
59
|
+
|
60
|
+
parse_line while next_line
|
61
|
+
|
62
|
+
reset
|
63
|
+
result
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# TODO: the markdown rendering should be a filter... that way
|
69
|
+
# you could have different kinds of FML markups
|
70
|
+
|
71
|
+
# TODO: rewrite this to have a sort of wrapping filter... have this just output:
|
72
|
+
# [:question, [:summary, [text]], [:data, [line, line...]]]
|
73
|
+
# [:answer, [:summary, [text]], [:data, [line, line...]]]
|
74
|
+
|
75
|
+
def parse_line
|
76
|
+
# start an answer block
|
77
|
+
if @line =~ /^a(nswer)?(\s*)\:\s*(.*?)$/i
|
78
|
+
asummary = @line.sub(/^a(nswer)?(\s*)\:(\s*)/i, '')
|
79
|
+
syntax_error!("Cannot begin an answer without a question") if @current != :q
|
80
|
+
@current = :a
|
81
|
+
|
82
|
+
@stacks.last.last << [:html, :tag, 'details', [:html, :attrs, [:html, :attr, 'class', [:static, 'answer']]], [:multi]]
|
83
|
+
@stacks.last.last.last.last << [:html, :tag, 'summary', [:multi], [:static, asummary]]
|
84
|
+
@stacks.last.last.last.last << [:html, :tag, 'div', [:multi], [:multi]]
|
85
|
+
|
86
|
+
elsif @line =~ /^q(uestion)?(\s*)\:\s*(.*?)$/i
|
87
|
+
qsummary = @line.sub(/^q(uestion)?(\s*)\:(\s*)/i, '')
|
88
|
+
@current = :q
|
89
|
+
|
90
|
+
@stacks << [:html, :tag, 'section', [:html, :attrs, [:html, :attr, 'class', [:static, 'qna']]],[:multi]]
|
91
|
+
@stacks.last.last << [:html, :tag, 'details', [:html, :attrs, [:html, :attr, 'class', [:static, 'question']]], [:multi]]
|
92
|
+
@stacks.last.last.last.last << [:html, :tag, 'summary', [:multi], [:static, qsummary]]
|
93
|
+
@stacks.last.last.last.last << [:html, :tag, 'div', [:multi], [:multi]]
|
94
|
+
|
95
|
+
else
|
96
|
+
indent = get_indent(@line)
|
97
|
+
if @base_indent == 0
|
98
|
+
@base_indent = indent
|
99
|
+
@strip_exp = %r"^\s{#{@base_indent}}"
|
100
|
+
end
|
101
|
+
|
102
|
+
if indent >= 1
|
103
|
+
# strip off base_indent from @line
|
104
|
+
@stacks.last.last.last.last.last.last << [:static, @line.sub("\t", @tab).sub(@strip_exp, '')]
|
105
|
+
@stacks.last.last.last.last.last.last << [:newline]
|
106
|
+
else
|
107
|
+
# done with the indented block
|
108
|
+
@base_indent = 0
|
109
|
+
end
|
110
|
+
|
111
|
+
@current = :q if @line.strip != ''
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def reset(lines = nil, stacks = nil)
|
116
|
+
@stacks = stacks
|
117
|
+
@base_indent = 0
|
118
|
+
@lineno = 0
|
119
|
+
@lines = lines
|
120
|
+
@line = @orig_line = nil
|
121
|
+
@current = nil
|
122
|
+
end
|
123
|
+
|
124
|
+
def next_line
|
125
|
+
if @lines.empty?
|
126
|
+
@orig_line = @line = nil
|
127
|
+
else
|
128
|
+
@orig_line = @lines.shift
|
129
|
+
@lineno += 1
|
130
|
+
@line = @orig_line.dup
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def get_indent(line)
|
135
|
+
# Figure out the indentation. ugly/slow/ ripped from Slim
|
136
|
+
line[/\A[ \t]*/].sub("\t", @tab).size
|
137
|
+
end
|
138
|
+
|
139
|
+
# Helper for raising exceptions
|
140
|
+
def syntax_error!(message, args = {})
|
141
|
+
args[:orig_line] ||= @orig_line
|
142
|
+
args[:line] ||= @line
|
143
|
+
args[:lineno] ||= @lineno
|
144
|
+
args[:column] ||= args[:orig_line] && args[:line] ? args[:orig_line].size - args[:line].size : 0
|
145
|
+
raise SyntaxError.new(message, options[:file],
|
146
|
+
args[:orig_line], args[:lineno], args[:column])
|
147
|
+
end
|
148
|
+
end
|
data/lib/fml/template.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module FAQML
|
2
|
+
# Tilt template implementation for FAQML
|
3
|
+
# @api public
|
4
|
+
Template = Temple::Templates::Tilt(FAQML::Engine, :register_as => :fml)
|
5
|
+
|
6
|
+
if Object.const_defined?(:Rails)
|
7
|
+
# Rails template implementation for FAQML
|
8
|
+
# @api public
|
9
|
+
RailsTemplate = Temple::Templates::Rails(FAQML::Engine,
|
10
|
+
:register_as => :fml,
|
11
|
+
# Use rails-specific generator. This is necessary
|
12
|
+
# to support block capturing and streaming.
|
13
|
+
:generator => Temple::Generators::RailsOutputBuffer,
|
14
|
+
# Rails takes care of the capturing by itself.
|
15
|
+
:disable_capture => true,
|
16
|
+
:streaming => Object.const_defined?(:Fiber))
|
17
|
+
end
|
18
|
+
end
|
data/lib/fml/version.rb
ADDED
data/test/test1.fml
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: faqml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-08-
|
12
|
+
date: 2012-08-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redcarpet
|
@@ -27,16 +27,54 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 2.1.1
|
30
|
-
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: temple
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.4.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.4.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: tilt
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.3.3
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.3.3
|
62
|
+
description: A Simple FAQ Markup Language
|
31
63
|
email: eric@basho.com
|
32
64
|
executables: []
|
33
65
|
extensions: []
|
34
66
|
extra_rdoc_files: []
|
35
67
|
files:
|
36
68
|
- Gemfile
|
69
|
+
- Gemfile.lock
|
37
70
|
- README.markdown
|
38
71
|
- faqml.gemspec
|
39
72
|
- lib/faqml.rb
|
73
|
+
- lib/fml/engine.rb
|
74
|
+
- lib/fml/parser.rb
|
75
|
+
- lib/fml/template.rb
|
76
|
+
- lib/fml/version.rb
|
77
|
+
- test/test1.fml
|
40
78
|
homepage: http://rubygems.org/gems/faqml
|
41
79
|
licenses: []
|
42
80
|
post_install_message:
|