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