marsdawn 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|