marsdawn 0.0.1
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 +15 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +20 -0
- data/.travis.yml +7 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +119 -0
- data/Rakefile +12 -0
- data/bin/marsdawn +6 -0
- data/lib/marsdawn/builder.rb +35 -0
- data/lib/marsdawn/command.rb +154 -0
- data/lib/marsdawn/config.rb +31 -0
- data/lib/marsdawn/search/rroonga.rb +83 -0
- data/lib/marsdawn/search.rb +16 -0
- data/lib/marsdawn/site/breadcrumb.rb +30 -0
- data/lib/marsdawn/site/indexer.rb +78 -0
- data/lib/marsdawn/site/link.rb +28 -0
- data/lib/marsdawn/site/page.rb +118 -0
- data/lib/marsdawn/site/page_nav.rb +24 -0
- data/lib/marsdawn/site/search_box.rb +27 -0
- data/lib/marsdawn/site/search_page.rb +35 -0
- data/lib/marsdawn/site.rb +70 -0
- data/lib/marsdawn/source/document.rb +36 -0
- data/lib/marsdawn/source/front_matter.rb +25 -0
- data/lib/marsdawn/source/kramdown/parser.rb +56 -0
- data/lib/marsdawn/source.rb +164 -0
- data/lib/marsdawn/storage/active_record/marsdawn.rb +20 -0
- data/lib/marsdawn/storage/active_record.rb +83 -0
- data/lib/marsdawn/storage/base.rb +44 -0
- data/lib/marsdawn/storage/file_system.rb +83 -0
- data/lib/marsdawn/storage/redis.rb +0 -0
- data/lib/marsdawn/storage/test.rb +20 -0
- data/lib/marsdawn/storage/test_not_implemented_error.rb +6 -0
- data/lib/marsdawn/storage.rb +22 -0
- data/lib/marsdawn/util.rb +39 -0
- data/lib/marsdawn/version.rb +3 -0
- data/lib/marsdawn.rake +9 -0
- data/lib/marsdawn.rb +55 -0
- data/marsdawn.gemspec +25 -0
- data/spec/_compiled_doc/.gitkeep +0 -0
- data/spec/_test_doc/config.yml +10 -0
- data/spec/_test_doc/docs01/.index.md +1 -0
- data/spec/_test_doc/docs01/.marsdawn.yml +4 -0
- data/spec/_test_doc/docs01/010_about.md +4 -0
- data/spec/_test_doc/docs01/020_tutorial/.index.md +0 -0
- data/spec/_test_doc/docs01/020_tutorial/010_install.md +6 -0
- data/spec/_test_doc/docs01/020_tutorial/020_getting_start.md +9 -0
- data/spec/_test_doc/docs01/030_reference/1up.md +0 -0
- data/spec/_test_doc/docs01/030_reference/each.md +0 -0
- data/spec/_test_doc/docs01/030_reference/z-index.md +0 -0
- data/spec/_test_doc/docs01/040_appendix.md +3 -0
- data/spec/_test_doc/no_config/.gitkeep +0 -0
- data/spec/_test_doc/no_key/.marsdawn.yml +3 -0
- data/spec/_test_doc/samples/010_sample-document.md +4 -0
- data/spec/_test_doc/samples/test_document.md +1 -0
- data/spec/_tmp/.gitkeep +0 -0
- data/spec/lib/marsdawn/command_spec.rb +144 -0
- data/spec/lib/marsdawn/config_spec.rb +41 -0
- data/spec/lib/marsdawn/site/page_spec.rb +122 -0
- data/spec/lib/marsdawn/site_spec.rb +67 -0
- data/spec/lib/marsdawn/source/document_spec.rb +178 -0
- data/spec/lib/marsdawn/source/front_matter_spec.rb +55 -0
- data/spec/lib/marsdawn/source_spec.rb +40 -0
- data/spec/lib/marsdawn/storage/active_record_spec.rb +47 -0
- data/spec/lib/marsdawn/storage/file_system_spec.rb +46 -0
- data/spec/lib/marsdawn/storage_spec.rb +29 -0
- data/spec/lib/marsdawn/util_spec.rb +57 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/stubs/groonga.rb +17 -0
- data/spec/stubs/marsdawn_docs.rb +40 -0
- metadata +203 -0
@@ -0,0 +1,118 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'marsdawn/site/breadcrumb'
|
4
|
+
require 'marsdawn/site/page_nav'
|
5
|
+
require 'marsdawn/site/search_box'
|
6
|
+
|
7
|
+
class Marsdawn::Site
|
8
|
+
class Page
|
9
|
+
attr_reader :site, :uri, :title, :content, :type, :level, :sysinfo
|
10
|
+
|
11
|
+
def initialize doc_info, page, site
|
12
|
+
@doc_info = doc_info
|
13
|
+
@site = site
|
14
|
+
front_matter = page[:front_matter]
|
15
|
+
@sysinfo = page[:sysinfo]
|
16
|
+
@uri = @sysinfo[:uri]
|
17
|
+
@title = front_matter[:title]
|
18
|
+
@content = page[:content]
|
19
|
+
@type = @sysinfo[:type]
|
20
|
+
@level = @sysinfo[:level]
|
21
|
+
@search_word = ''
|
22
|
+
end
|
23
|
+
|
24
|
+
def breadcrumb
|
25
|
+
@breadcrumb ||= Marsdawn::Site::Breadcrumb.new(@site, @sysinfo[:breadcrumb])
|
26
|
+
end
|
27
|
+
|
28
|
+
def neighbor
|
29
|
+
@neighbor ||= @site.index.neighbor(@uri)
|
30
|
+
end
|
31
|
+
|
32
|
+
def top
|
33
|
+
@site.top
|
34
|
+
end
|
35
|
+
|
36
|
+
def parent
|
37
|
+
@parent ||= Marsdawn::Site::Link.new(@site, @sysinfo[:parent]) unless @sysinfo[:parent].nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
def under
|
41
|
+
@under ||= @site.index.under(@uri)
|
42
|
+
end
|
43
|
+
|
44
|
+
def page_nav
|
45
|
+
@page_nav ||= Marsdawn::Site::PageNav.new(@site, @sysinfo[:prev_page], @sysinfo[:next_page])
|
46
|
+
end
|
47
|
+
|
48
|
+
def link
|
49
|
+
@link ||= Marsdawn::Site::Link.new(@site, @uri, @title)
|
50
|
+
end
|
51
|
+
|
52
|
+
def search_box
|
53
|
+
@search_box ||= Marsdawn::Site::SearchBox.new(@site, @search_word)
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_s
|
57
|
+
to_html
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_html options={}
|
61
|
+
<<-"EOS"
|
62
|
+
<div class="main-container">
|
63
|
+
<div class="container">
|
64
|
+
<div class="row">
|
65
|
+
<div class="col-lg-3 sidebar">
|
66
|
+
<nav>#{neighbor}</nav>
|
67
|
+
</div>
|
68
|
+
<div class="col-lg-9 content">
|
69
|
+
<p>contents</p>
|
70
|
+
</div>
|
71
|
+
</div>
|
72
|
+
</div>
|
73
|
+
</div>
|
74
|
+
EOS
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_page_html options={}
|
78
|
+
opts = {
|
79
|
+
css: ['http://dev.screw-axis.com/marsdawn/style.css'],
|
80
|
+
js: [],
|
81
|
+
title: CGI.escapeHTML(@title),
|
82
|
+
title_suffix: " | #{CGI.escapeHTML(@site.title)}",
|
83
|
+
lang: @doc_info[:lang],
|
84
|
+
charset: @doc_info[:encoding]
|
85
|
+
}.merge(options)
|
86
|
+
css_html = opts[:css].map{|file| %!<link rel="stylesheet" href="#{file}" type="text/css" />!}.join("\n")
|
87
|
+
js_html = opts[:js].map{|file| %!<script src="#{file}"></script>!}.join("\n")
|
88
|
+
%|<!DOCTYPE html>
|
89
|
+
<html lang="#{opts[:lang]}">
|
90
|
+
<head>
|
91
|
+
<meta charset="#{opts[:encoding]}" />
|
92
|
+
<title>#{opts[:title]}#{opts[:title_suffix]}</title>
|
93
|
+
#{css_html}
|
94
|
+
#{js_html}
|
95
|
+
</head>
|
96
|
+
<body>
|
97
|
+
<div id="container">
|
98
|
+
<div id="site-header"><h1 id="site-title">#{@site.title_link}</h1></div>
|
99
|
+
<div id="side-menu">
|
100
|
+
#{search_box}
|
101
|
+
<nav>#{neighbor}</nav>
|
102
|
+
</div>
|
103
|
+
<div id="site-body">
|
104
|
+
<div id="main-content">
|
105
|
+
<div id="breadcrumb"><nav>#{breadcrumb}</nav></div>
|
106
|
+
<div id="page-content">#{@content}</div>
|
107
|
+
<div id="under-index">#{under}</div>
|
108
|
+
<div id="page-nav">#{page_nav}</div>
|
109
|
+
</div>
|
110
|
+
<div class="clear"></div>
|
111
|
+
</div>
|
112
|
+
</div>
|
113
|
+
</body>
|
114
|
+
</html>|
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Marsdawn::Site
|
4
|
+
class PageNav < Hash
|
5
|
+
|
6
|
+
def initialize site, prev_page, next_page
|
7
|
+
@site = site
|
8
|
+
self[:prev_page] = @site.index[prev_page] unless prev_page.nil?
|
9
|
+
self[:next_page] = @site.index[next_page] unless next_page.nil?
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
to_html
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_html
|
17
|
+
%!<ul>
|
18
|
+
<li id="nav-prev-page">#{self[:prev_page]}</li>
|
19
|
+
<li id="nav-next-page">#{self[:next_page]}</li>
|
20
|
+
</ul>!
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Marsdawn::Site
|
4
|
+
class SearchBox
|
5
|
+
|
6
|
+
def initialize site, word
|
7
|
+
@site = site
|
8
|
+
@word = word
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
to_html
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_html
|
16
|
+
if @site.searchable?
|
17
|
+
%|<div id="search-box">
|
18
|
+
<form action="#{@site.search_path}" method="get">
|
19
|
+
<input type="search" name="search" value="#{Marsdawn::Util.html_escape(@word)}" />
|
20
|
+
</form>
|
21
|
+
</div>|
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Marsdawn::Site
|
4
|
+
class SearchPage < Page
|
5
|
+
|
6
|
+
def initialize doc_info, site, search_word, search_results
|
7
|
+
@word = search_word
|
8
|
+
@results = search_results
|
9
|
+
exvars = {title: 'Search Results'}
|
10
|
+
sysinfo = {
|
11
|
+
uri: site.search_path,
|
12
|
+
breadcrumb: [],
|
13
|
+
prev_page: nil,
|
14
|
+
next_page: nil
|
15
|
+
}
|
16
|
+
page = {
|
17
|
+
content: content,
|
18
|
+
exvars: exvars,
|
19
|
+
sysinfo: sysinfo
|
20
|
+
}
|
21
|
+
super doc_info, page, site
|
22
|
+
@search_word = search_word
|
23
|
+
end
|
24
|
+
|
25
|
+
def content
|
26
|
+
ret = %!<h1>Search Results for '#{Marsdawn::Util.html_escape(@word)}'</h1>!
|
27
|
+
@content ||= @results.each_with_object ret do |res, ret|
|
28
|
+
ret << %!<h4><a href=".#{res[:uri]}">#{res[:title]}</a></h4>!
|
29
|
+
blocks = res[:results].join(' ... ')
|
30
|
+
ret << %!<div class="search-result">#{blocks}</div>!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'marsdawn/site/page'
|
4
|
+
require 'marsdawn/site/search_page'
|
5
|
+
require 'marsdawn/site/link'
|
6
|
+
require 'marsdawn/site/indexer'
|
7
|
+
require 'cgi'
|
8
|
+
|
9
|
+
class Marsdawn::Site
|
10
|
+
|
11
|
+
def initialize opts, config=nil
|
12
|
+
@key = opts[:key]
|
13
|
+
if block_given?
|
14
|
+
config = {}
|
15
|
+
yield config
|
16
|
+
elsif config.nil?
|
17
|
+
config = Marsdawn::Config.new.to_hash(@key)
|
18
|
+
end
|
19
|
+
@config = config
|
20
|
+
@storage = Marsdawn::Storage.get(@config[:storage], opts)
|
21
|
+
@doc_info = @storage.get_document_info
|
22
|
+
@opts = opts
|
23
|
+
@base_path = (opts.key?(:base_path) ? opts[:base_path] : '')
|
24
|
+
end
|
25
|
+
|
26
|
+
def top
|
27
|
+
@top ||= page('/')
|
28
|
+
end
|
29
|
+
|
30
|
+
def page uri
|
31
|
+
Marsdawn::Site::Page.new @doc_info, @storage.get(uri), self
|
32
|
+
end
|
33
|
+
|
34
|
+
def full_path uri
|
35
|
+
File.join @base_path, uri
|
36
|
+
end
|
37
|
+
|
38
|
+
def index
|
39
|
+
@index ||= Marsdawn::Site::Indexer.new(self, @doc_info[:site_index])
|
40
|
+
end
|
41
|
+
|
42
|
+
def page_title uri
|
43
|
+
@doc_info[:site_index][uri] if @doc_info[:site_index].key?(uri)
|
44
|
+
end
|
45
|
+
|
46
|
+
def title
|
47
|
+
@doc_info[:title]
|
48
|
+
end
|
49
|
+
|
50
|
+
def title_link
|
51
|
+
@title_link ||= Marsdawn::Site::Link.new(self, "#{@base_path}/", title)
|
52
|
+
end
|
53
|
+
|
54
|
+
def searchable?
|
55
|
+
@config.key?(:search)
|
56
|
+
end
|
57
|
+
|
58
|
+
def search_path
|
59
|
+
uri = @opts[:search_uri] || '/'
|
60
|
+
full_path uri
|
61
|
+
end
|
62
|
+
|
63
|
+
def search keyword, opts={}
|
64
|
+
raise "No search settings for this document." unless searchable?
|
65
|
+
se = Marsdawn::Search.get(@storage, @config[:search])
|
66
|
+
results = se.search(keyword, opts)
|
67
|
+
Marsdawn::Site::SearchPage.new @doc_info, self, keyword, results
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'kramdown'
|
4
|
+
require 'marsdawn/source/front_matter'
|
5
|
+
require 'marsdawn/source/kramdown/parser'
|
6
|
+
|
7
|
+
class Marsdawn::Source
|
8
|
+
class Document < Kramdown::Document
|
9
|
+
|
10
|
+
attr_accessor :front_matter
|
11
|
+
|
12
|
+
def self.read path, title=nil, options={}
|
13
|
+
text = open(path).read
|
14
|
+
new(text, options).tap do |doc|
|
15
|
+
title = File.basename(path, '.*') if title.nil?
|
16
|
+
title = title.gsub(/^\d+_/, '').gsub(/_|-/, ' ').capitalize
|
17
|
+
doc.front_matter[:title] ||= title
|
18
|
+
doc.front_matter[:link_key] ||= title
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize text, options={}
|
23
|
+
doc = FrontMatter.new(text)
|
24
|
+
@front_matter = {}
|
25
|
+
options[:input] = 'Marsdawn'
|
26
|
+
super doc.content, options
|
27
|
+
@front_matter.merge! Kramdown::Parser::Marsdawn.front_matter
|
28
|
+
@front_matter.merge! doc.attr
|
29
|
+
end
|
30
|
+
|
31
|
+
def front_matter
|
32
|
+
@front_matter
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Marsdawn
|
6
|
+
class Source
|
7
|
+
class FrontMatter
|
8
|
+
|
9
|
+
attr_reader :attr, :content
|
10
|
+
|
11
|
+
def initialize text
|
12
|
+
m = text.match(/\A---*\n(?<attr>.*?)\n---*\n(?<content>.*)$/m)
|
13
|
+
if m.nil?
|
14
|
+
@attr = {}
|
15
|
+
@content = text
|
16
|
+
else
|
17
|
+
@attr = Marsdawn::Util.hash_symbolize_keys_deep(YAML.load(m[:attr]))
|
18
|
+
raise 'Invalid front-matter format.' unless @attr.kind_of?(Hash)
|
19
|
+
@content = m[:content]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Kramdown::Parser::Marsdawn < Kramdown::Parser::Kramdown
|
4
|
+
|
5
|
+
def self.front_matter
|
6
|
+
@@front_matter
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse
|
10
|
+
@@front_matter = {}
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse_setext_header
|
15
|
+
ret = super
|
16
|
+
handle_header @src[1], @src[3].to_i
|
17
|
+
ret
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_atx_header
|
21
|
+
ret = super
|
22
|
+
handle_header @src[2].to_s.strip, @src[1].length
|
23
|
+
ret
|
24
|
+
end
|
25
|
+
|
26
|
+
def handle_extension(name, opts, body, type)
|
27
|
+
case name
|
28
|
+
when 'front_matter'
|
29
|
+
opts.each do |key, val|
|
30
|
+
@@front_matter[key.to_sym] = val
|
31
|
+
@@front_matter[:link_key] = val if key == 'title'
|
32
|
+
end
|
33
|
+
true
|
34
|
+
else
|
35
|
+
super name, opts, body, type
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def handle_header title, level
|
41
|
+
add_title_vars title
|
42
|
+
insert_title_anchor title if level < 4
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_title_vars title
|
46
|
+
@@front_matter[:title] = title unless @@front_matter.key?(:title)
|
47
|
+
@@front_matter[:link_key] = title unless @@front_matter.key?(:link_key)
|
48
|
+
end
|
49
|
+
|
50
|
+
def insert_title_anchor title
|
51
|
+
anchor_name = title.downcase.gsub(' ', '-')
|
52
|
+
@tree.children.last.children.insert 0, Element.new(:raw, %!<a name="#{anchor_name}"></a>!, 'type' => 'html')
|
53
|
+
@@front_matter[:anchors] ||= {}
|
54
|
+
@@front_matter[:anchors][title] = anchor_name
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'marsdawn/source/document'
|
5
|
+
|
6
|
+
class Marsdawn::Source
|
7
|
+
|
8
|
+
attr_reader :doc_info, :local2uri
|
9
|
+
|
10
|
+
def initialize path
|
11
|
+
@path = File.expand_path(path)
|
12
|
+
raise "No source directory '#{@path}'." unless File.exists?(@path)
|
13
|
+
@doc_info = {
|
14
|
+
key: nil,
|
15
|
+
lang: 'en',
|
16
|
+
title: '',
|
17
|
+
version: '0.0.0',
|
18
|
+
markdown_extname: '.md',
|
19
|
+
directory_index: '.index.md',
|
20
|
+
encoding: 'utf-8',
|
21
|
+
link_defs: {},
|
22
|
+
kramdown_options: {}
|
23
|
+
}
|
24
|
+
load_doc_info
|
25
|
+
investigate
|
26
|
+
end
|
27
|
+
|
28
|
+
def each_contents options
|
29
|
+
@local2uri.each do |file, uri|
|
30
|
+
yield uri, markdown(file, uri, options), @front_matter[uri], @sysinfo[uri]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
def load_doc_info
|
36
|
+
doc_info_file = File.join(@path, '.marsdawn.yml')
|
37
|
+
raise "There is no doc_info file '.marsdawn.yml' in '#{@path}'" unless File.exists?(doc_info_file)
|
38
|
+
conf = Marsdawn::Util.hash_symbolize_keys(YAML.load_file(doc_info_file))
|
39
|
+
@doc_info.merge! conf
|
40
|
+
@doc_key = @doc_info[:key]
|
41
|
+
raise "The document key should be specified in .marsdawn.yml." if @doc_key.nil? || @doc_key.empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
def investigate
|
45
|
+
@local2uri = {}
|
46
|
+
@front_matter = {}
|
47
|
+
@sysinfo = {}
|
48
|
+
digg @path
|
49
|
+
update_sysinfo
|
50
|
+
update_docinfo
|
51
|
+
end
|
52
|
+
|
53
|
+
def link_defs base_uri
|
54
|
+
ret = relative_link_defs_from_front_matter base_uri
|
55
|
+
base_path = Pathname(File.dirname(base_uri))
|
56
|
+
@doc_info[:link_defs].each do |key, link|
|
57
|
+
uri, title = link
|
58
|
+
if uri.start_with?('/')
|
59
|
+
rel_path = Pathname(uri).relative_path_from(base_path).to_s
|
60
|
+
else
|
61
|
+
rel_path = uri
|
62
|
+
end
|
63
|
+
ret[key] = [rel_path, title]
|
64
|
+
end
|
65
|
+
ret
|
66
|
+
end
|
67
|
+
|
68
|
+
def relative_link_defs_from_front_matter base_uri
|
69
|
+
base_path = Pathname(File.dirname(base_uri))
|
70
|
+
@front_matter.each_with_object({}) do |(uri, vars), defs|
|
71
|
+
if vars.key?(:link_key)
|
72
|
+
rel_path = Pathname(uri).relative_path_from(base_path).to_s
|
73
|
+
defs[vars[:link_key]] = [rel_path, vars[:title]]
|
74
|
+
if vars.key?(:anchors)
|
75
|
+
vars[:anchors].each do |title, anchor_name|
|
76
|
+
defs["#{vars[:link_key]} - #{title}"] = ["#{rel_path}\##{anchor_name}", title]
|
77
|
+
defs[title] = ["\##{anchor_name}", title] if base_uri == uri
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def markdown file, uri, opts
|
85
|
+
opts[:link_defs] = link_defs(uri)
|
86
|
+
Document.read(file, @front_matter[uri][:title], opts).to_html
|
87
|
+
end
|
88
|
+
|
89
|
+
def digg path, uri=''
|
90
|
+
items = Dir.glob(File.join(path,'*')).map{|f| File.basename(f)}.sort
|
91
|
+
read_directory_index path, uri
|
92
|
+
items.each do |item|
|
93
|
+
fullpath = File.join(path, item)
|
94
|
+
uri_item = (item =~ /^\d+_(.*)/ ? $1 : item)
|
95
|
+
if File.directory?(fullpath)
|
96
|
+
digg fullpath, "#{uri}/#{uri_item}"
|
97
|
+
elsif uri_item != @doc_info[:directory_index]
|
98
|
+
set_page_info fullpath, uri, uri_item
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_page_info fullpath, uri, uri_item
|
104
|
+
extname = File.extname(uri_item)
|
105
|
+
if extname == @doc_info[:markdown_extname]
|
106
|
+
uri_item = File.basename(uri_item, extname)
|
107
|
+
fulluri = "#{uri}/#{uri_item}"
|
108
|
+
@local2uri[fullpath] = fulluri
|
109
|
+
@front_matter[fulluri] = read_front_matter(fullpath, uri_item)
|
110
|
+
@sysinfo[fulluri] = {:type => 'folder'}
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def read_directory_index path, uri
|
115
|
+
indexfile = File.join(path, @doc_info[:directory_index])
|
116
|
+
if File.exists?(indexfile)
|
117
|
+
uri = (uri == '' ? '/' : uri)
|
118
|
+
@local2uri[indexfile] = uri
|
119
|
+
@front_matter[uri] = read_front_matter(indexfile, File.basename(uri))
|
120
|
+
@sysinfo[uri] = {:type => 'page'}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def read_front_matter file, name
|
125
|
+
doc = Document.read(file, name, @doc_info[:kramdown_options])
|
126
|
+
doc.front_matter
|
127
|
+
end
|
128
|
+
|
129
|
+
def update_sysinfo
|
130
|
+
prev_page = nil
|
131
|
+
@sysinfo.each do |uri, info|
|
132
|
+
@sysinfo[uri][:uri] = uri
|
133
|
+
@sysinfo[uri][:level] = uri.count('/')
|
134
|
+
@sysinfo[uri][:breadcrumb] = create_breadcrumb(uri)
|
135
|
+
@sysinfo[uri][:parent] = @sysinfo[uri][:breadcrumb].last
|
136
|
+
@sysinfo[uri][:prev_page] = nil
|
137
|
+
@sysinfo[uri][:next_page] = nil
|
138
|
+
unless prev_page.nil?
|
139
|
+
@sysinfo[uri][:prev_page] = prev_page
|
140
|
+
@sysinfo[prev_page][:next_page] = uri
|
141
|
+
end
|
142
|
+
prev_page = uri
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def update_docinfo
|
147
|
+
@doc_info[:site_index] = create_site_index
|
148
|
+
end
|
149
|
+
|
150
|
+
def create_breadcrumb uri
|
151
|
+
uri.split('/').each_with_object([]) do |dir, ret|
|
152
|
+
parent = (ret.last == '/' ? '' : ret.last)
|
153
|
+
path = "#{parent}/#{dir}"
|
154
|
+
ret << path unless uri == path
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def create_site_index
|
159
|
+
@front_matter.each_with_object({}) do |(uri, vars), ret|
|
160
|
+
ret[uri] = vars[:title]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Marsdawn::Storage::ActiveRecord::Marsdawn < ActiveRecord::Base
|
4
|
+
self.table_name = 'marsdawn'
|
5
|
+
attr_accessor :doc, :lang, :version, :uri, :data
|
6
|
+
|
7
|
+
def self.create_unless_exists
|
8
|
+
unless ActiveRecord::Base.connection.table_exists? self.table_name
|
9
|
+
ActiveRecord::Migration.create_table :marsdawn do |t|
|
10
|
+
t.string :doc, :null => false
|
11
|
+
t.string :lang, :null => false
|
12
|
+
t.string :version, :null => false
|
13
|
+
t.string :uri, :null => false
|
14
|
+
t.text :data
|
15
|
+
end
|
16
|
+
ActiveRecord::Migration.add_index :marsdawn, [:doc, :lang, :version, :uri], :unique => true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'marsdawn/storage/base'
|
4
|
+
require 'json'
|
5
|
+
require 'zlib'
|
6
|
+
|
7
|
+
class Marsdawn::Storage::ActiveRecord < Marsdawn::Storage::Base
|
8
|
+
|
9
|
+
def initialize config, opts={}
|
10
|
+
unless Module.const_defined?('ActiveRecord')
|
11
|
+
raise "Gem 'active_record' should be installed to use ActiveRecord storage for MarsDawn." unless require 'active_record'
|
12
|
+
end
|
13
|
+
#require 'marsdawn/storage/active_record/marsdawn'
|
14
|
+
@opts = {
|
15
|
+
key: '-',
|
16
|
+
lang: 'en',
|
17
|
+
version: '0.0.1'
|
18
|
+
}.merge(opts)
|
19
|
+
end
|
20
|
+
|
21
|
+
def prepare
|
22
|
+
#Marsdawn.create_unless_exists
|
23
|
+
@stack = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def finalize
|
27
|
+
MarsdawnDocs.transaction do
|
28
|
+
MarsdawnDocs.where(selector).each do |page|
|
29
|
+
MarsdawnDocs.delete_all(selector(uri: page.uri)) unless @stack.key?(page.uri)
|
30
|
+
end
|
31
|
+
@stack.each do |uri, data|
|
32
|
+
rec = MarsdawnDocs.where(selector(uri: uri))
|
33
|
+
if rec.size > 0
|
34
|
+
rec.first.update_attributes! data: compress(data)
|
35
|
+
else
|
36
|
+
MarsdawnDocs.create selector(uri: uri, data: compress(data))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
rescue => e
|
41
|
+
raise e
|
42
|
+
end
|
43
|
+
|
44
|
+
def clean_up
|
45
|
+
@stack = {}
|
46
|
+
end
|
47
|
+
|
48
|
+
def set_document_info doc_info
|
49
|
+
@stack['doc_info'] = doc_info
|
50
|
+
end
|
51
|
+
|
52
|
+
def set uri, content, front_matter, sysinfo
|
53
|
+
@stack[uri] = {content: content, front_matter: front_matter, sysinfo: sysinfo}
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_document_info
|
57
|
+
Marsdawn::Util.hash_symbolize_keys(get_data('doc_info'))
|
58
|
+
end
|
59
|
+
|
60
|
+
def get uri
|
61
|
+
data = get_data(uri)
|
62
|
+
Marsdawn::Util.hash_symbolize_keys_deep(data) unless data.nil?
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def compress data
|
67
|
+
Zlib::Deflate.deflate(JSON.generate(data))
|
68
|
+
end
|
69
|
+
|
70
|
+
def decompress data
|
71
|
+
JSON.parse(Zlib::Inflate.inflate(data))
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_data uri
|
75
|
+
page = MarsdawnDocs.where(selector(uri: uri)).first
|
76
|
+
decompress(page.data) unless page.nil?
|
77
|
+
end
|
78
|
+
|
79
|
+
def selector additional={}
|
80
|
+
{key: @opts[:key], lang: @opts[:lang], version: @opts[:version]}.merge(additional)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|