faqml 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +24 -29
- data/lib/faqml.rb +5 -0
- data/lib/fml/details_filter.rb +34 -0
- data/lib/fml/engine.rb +2 -1
- data/lib/fml/markdown_filter.rb +5 -0
- data/lib/fml/parser.rb +11 -31
- data/lib/fml/version.rb +1 -1
- data/lib/fml/wrap_filter.rb +43 -0
- data/test/test0.fml +4 -0
- metadata +6 -2
data/README.markdown
CHANGED
@@ -1,51 +1,46 @@
|
|
1
1
|
# FML
|
2
2
|
|
3
|
-
The FAQ Markup Language. It's pretty simple. The gem is called `faqml`, so first install it.
|
3
|
+
The FAQ Markup Language. It's pretty simple. The gem is called `faqml`, so first install it. Then just access it through Tilt.
|
4
4
|
|
5
5
|
```ruby
|
6
6
|
require 'rubygems'
|
7
7
|
require 'faqml'
|
8
|
-
|
9
|
-
puts
|
8
|
+
require 'tilt'
|
9
|
+
puts Tilt.new('mypage.fml').render
|
10
10
|
```
|
11
11
|
|
12
|
-
Your markup in `mypage.fml` should be valid markdown. The only difference is, that QnA blocks are started by
|
12
|
+
Your markup in `mypage.fml` should be valid markdown. The only difference is, that QnA blocks are started by the keyword "question:", or "Q:", and answers being with "answer:" or "A:". If there is any text on the line after the question or answer keywords, it will be that section's summary (neither are required). All remaining indented text consitute the body of the question of answer.
|
13
13
|
|
14
14
|
This FML
|
15
15
|
|
16
16
|
```fml
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
Do Bears eat Beats?
|
25
|
-
===
|
26
|
-
Of course.
|
17
|
+
question: What kind of Bear is Best?
|
18
|
+
I hear there are basically two school of thought.
|
19
|
+
answer:
|
20
|
+
False. *Blackbear*.
|
21
|
+
|
22
|
+
Q: Do Bears eat Beats?
|
23
|
+
A: Of course.
|
27
24
|
```
|
28
25
|
|
29
26
|
Produces this HTML
|
30
27
|
|
31
28
|
```html
|
32
29
|
<section class="qna">
|
33
|
-
<
|
34
|
-
|
35
|
-
<div
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
</div>
|
41
|
-
</div>
|
30
|
+
<details class="question" open>
|
31
|
+
<summary>What kind of Bear is Best?</summary>
|
32
|
+
<div>I hear there are basically two school of thought</div>
|
33
|
+
<details class="answer" open>
|
34
|
+
<div>False. *Blackbear*</div>
|
35
|
+
</details>
|
36
|
+
</details>
|
42
37
|
</section>
|
43
38
|
<section class="qna">
|
44
|
-
<
|
45
|
-
|
46
|
-
<
|
47
|
-
<
|
48
|
-
</
|
49
|
-
</
|
39
|
+
<details class="question" open>
|
40
|
+
<summary>Do Bears eat Beats?</summary>
|
41
|
+
<details class="answer" open>
|
42
|
+
<summary>Of course.</summary>
|
43
|
+
</details>
|
44
|
+
</details>
|
50
45
|
</section>
|
51
46
|
```
|
data/lib/faqml.rb
CHANGED
@@ -4,5 +4,10 @@
|
|
4
4
|
require 'temple'
|
5
5
|
require 'fml/version'
|
6
6
|
require 'fml/parser'
|
7
|
+
require 'fml/wrap_filter'
|
8
|
+
require 'fml/details_filter'
|
7
9
|
require 'fml/engine'
|
8
10
|
require 'fml/template'
|
11
|
+
|
12
|
+
# module FAQML;end; require 'pp'; require 'temple'; %w{parser details_filter wrap_filter engine template}.each {|x| require "./lib/fml/#{x}"}
|
13
|
+
# puts Tilt.new('test/test0.fml').render
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class FAQML::DetailsFilter < Temple::Filter
|
2
|
+
def initialize(options = {})
|
3
|
+
@options = options
|
4
|
+
end
|
5
|
+
|
6
|
+
def call(exp)
|
7
|
+
compile(exp)
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_fml_qna(question, answer=nil)
|
11
|
+
answer_sexp = !answer.nil? && answer.length >= 4 ? build_fml_details('answer', answer[2], answer[3]) : nil
|
12
|
+
question_sexp = build_fml_details('question', question[2], question[3])
|
13
|
+
question_sexp.last << answer_sexp unless answer_sexp.nil?
|
14
|
+
[:html, :tag, 'section', [:html, :attrs, [:html, :attr, 'class', [:static, 'qna']]],
|
15
|
+
question_sexp
|
16
|
+
]
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def build_fml_details(class_name, summary, details)
|
22
|
+
sexp = [:html, :tag, 'details',
|
23
|
+
[:html, :attrs,
|
24
|
+
[:html, :attr, 'class', [:static, class_name]]],
|
25
|
+
[:multi]]
|
26
|
+
if !summary.last.first.nil? && summary.last.first != ''
|
27
|
+
sexp.last << [:html, :tag, 'summary', [:multi], [:static, summary.last.first]]
|
28
|
+
end
|
29
|
+
if details.length > 0
|
30
|
+
sexp.last << [:html, :tag, 'div', [:multi], [:multi, *details.last]]
|
31
|
+
end
|
32
|
+
sexp
|
33
|
+
end
|
34
|
+
end
|
data/lib/fml/engine.rb
CHANGED
@@ -12,7 +12,8 @@ class FAQML::Engine < Temple::Engine
|
|
12
12
|
|
13
13
|
# TODO: replace inline generation with a markdown filter
|
14
14
|
# use FAQML::MarkdownFilter
|
15
|
-
|
15
|
+
use FAQML::WrapFilter
|
16
|
+
use FAQML::DetailsFilter
|
16
17
|
|
17
18
|
html :AttributeMerger, :attr_delimiter
|
18
19
|
html :AttributeSorter, :sort_attrs
|
data/lib/fml/parser.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'redcarpet'
|
2
|
-
|
3
1
|
# Transforms FML into a Temple expression
|
4
2
|
# @api private
|
5
3
|
class FAQML::Parser
|
@@ -32,11 +30,6 @@ class FAQML::Parser
|
|
32
30
|
|
33
31
|
def initialize(options = {})
|
34
32
|
super
|
35
|
-
|
36
|
-
renderer = Redcarpet::Render::HTML.new
|
37
|
-
extensions = {fenced_code_blocks: true}
|
38
|
-
@markdown = Redcarpet::Markdown.new(renderer, extensions)
|
39
|
-
|
40
33
|
@tab = ' ' * @options[:tabsize]
|
41
34
|
end
|
42
35
|
|
@@ -65,32 +58,18 @@ class FAQML::Parser
|
|
65
58
|
|
66
59
|
private
|
67
60
|
|
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
61
|
def parse_line
|
76
62
|
# start an answer block
|
77
63
|
if @line =~ /^a(nswer)?(\s*)\:\s*(.*?)$/i
|
78
64
|
asummary = @line.sub(/^a(nswer)?(\s*)\:(\s*)/i, '')
|
79
65
|
syntax_error!("Cannot begin an answer without a question") if @current != :q
|
80
66
|
@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]]
|
67
|
+
@stacks << [:fml, :answer, [:fml, :summary, [asummary]], [:fml, :details, []] ]
|
85
68
|
|
86
69
|
elsif @line =~ /^q(uestion)?(\s*)\:\s*(.*?)$/i
|
87
70
|
qsummary = @line.sub(/^q(uestion)?(\s*)\:(\s*)/i, '')
|
88
71
|
@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]]
|
72
|
+
@stacks << [:fml, :question, [:fml, :summary, [qsummary]], [:fml, :details, []]]
|
94
73
|
|
95
74
|
else
|
96
75
|
indent = get_indent(@line)
|
@@ -99,16 +78,18 @@ class FAQML::Parser
|
|
99
78
|
@strip_exp = %r"^\s{#{@base_indent}}"
|
100
79
|
end
|
101
80
|
|
102
|
-
if indent >=
|
81
|
+
if @base_indent > 0 && indent >= @base_indent && !@current.nil?
|
103
82
|
# strip off base_indent from @line
|
104
|
-
@stacks.last.last.last
|
105
|
-
@stacks.last.last.last
|
106
|
-
|
83
|
+
@stacks.last.last.last << [:static, @line.sub("\t", @tab).sub(@strip_exp, '')]
|
84
|
+
@stacks.last.last.last << [:newline]
|
85
|
+
elsif @line.strip != ''
|
107
86
|
# done with the indented block
|
108
87
|
@base_indent = 0
|
109
|
-
|
88
|
+
@current = nil
|
110
89
|
|
111
|
-
|
90
|
+
@stacks << [:static, @line]
|
91
|
+
@stacks << [:newline]
|
92
|
+
end
|
112
93
|
end
|
113
94
|
end
|
114
95
|
|
@@ -142,7 +123,6 @@ class FAQML::Parser
|
|
142
123
|
args[:line] ||= @line
|
143
124
|
args[:lineno] ||= @lineno
|
144
125
|
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])
|
126
|
+
raise SyntaxError.new(message, options[:file], args[:orig_line], args[:lineno], args[:column])
|
147
127
|
end
|
148
128
|
end
|
data/lib/fml/version.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
# organizes the FAQs into a wrapped-block layout
|
2
|
+
class FAQML::WrapFilter < Temple::Filter
|
3
|
+
|
4
|
+
class SexpError < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(exp)
|
12
|
+
compile(exp)
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_multi(*exps)
|
16
|
+
result = [:multi]
|
17
|
+
|
18
|
+
current_block = nil
|
19
|
+
|
20
|
+
exps.each do |exp|
|
21
|
+
exp = compile(exp)
|
22
|
+
|
23
|
+
if exp.length > 2 && exp[0] == :fml
|
24
|
+
case exp[1]
|
25
|
+
when :question
|
26
|
+
current_block = [:fml, :qna, exp]
|
27
|
+
result << current_block
|
28
|
+
when :answer
|
29
|
+
raise SexpError, "Cannot create an answer without a question" if current_block.nil?
|
30
|
+
result.last << exp
|
31
|
+
current_block = nil
|
32
|
+
else
|
33
|
+
result << exp
|
34
|
+
end
|
35
|
+
next
|
36
|
+
end
|
37
|
+
|
38
|
+
result << exp
|
39
|
+
end
|
40
|
+
|
41
|
+
result
|
42
|
+
end
|
43
|
+
end
|
data/test/test0.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.
|
4
|
+
version: 0.2.0
|
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-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redcarpet
|
@@ -70,10 +70,14 @@ files:
|
|
70
70
|
- README.markdown
|
71
71
|
- faqml.gemspec
|
72
72
|
- lib/faqml.rb
|
73
|
+
- lib/fml/details_filter.rb
|
73
74
|
- lib/fml/engine.rb
|
75
|
+
- lib/fml/markdown_filter.rb
|
74
76
|
- lib/fml/parser.rb
|
75
77
|
- lib/fml/template.rb
|
76
78
|
- lib/fml/version.rb
|
79
|
+
- lib/fml/wrap_filter.rb
|
80
|
+
- test/test0.fml
|
77
81
|
- test/test1.fml
|
78
82
|
homepage: http://rubygems.org/gems/faqml
|
79
83
|
licenses: []
|