marsdawn 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +15 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +20 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +8 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +119 -0
  8. data/Rakefile +12 -0
  9. data/bin/marsdawn +6 -0
  10. data/lib/marsdawn/builder.rb +35 -0
  11. data/lib/marsdawn/command.rb +154 -0
  12. data/lib/marsdawn/config.rb +31 -0
  13. data/lib/marsdawn/search/rroonga.rb +83 -0
  14. data/lib/marsdawn/search.rb +16 -0
  15. data/lib/marsdawn/site/breadcrumb.rb +30 -0
  16. data/lib/marsdawn/site/indexer.rb +78 -0
  17. data/lib/marsdawn/site/link.rb +28 -0
  18. data/lib/marsdawn/site/page.rb +118 -0
  19. data/lib/marsdawn/site/page_nav.rb +24 -0
  20. data/lib/marsdawn/site/search_box.rb +27 -0
  21. data/lib/marsdawn/site/search_page.rb +35 -0
  22. data/lib/marsdawn/site.rb +70 -0
  23. data/lib/marsdawn/source/document.rb +36 -0
  24. data/lib/marsdawn/source/front_matter.rb +25 -0
  25. data/lib/marsdawn/source/kramdown/parser.rb +56 -0
  26. data/lib/marsdawn/source.rb +164 -0
  27. data/lib/marsdawn/storage/active_record/marsdawn.rb +20 -0
  28. data/lib/marsdawn/storage/active_record.rb +83 -0
  29. data/lib/marsdawn/storage/base.rb +44 -0
  30. data/lib/marsdawn/storage/file_system.rb +83 -0
  31. data/lib/marsdawn/storage/redis.rb +0 -0
  32. data/lib/marsdawn/storage/test.rb +20 -0
  33. data/lib/marsdawn/storage/test_not_implemented_error.rb +6 -0
  34. data/lib/marsdawn/storage.rb +22 -0
  35. data/lib/marsdawn/util.rb +39 -0
  36. data/lib/marsdawn/version.rb +3 -0
  37. data/lib/marsdawn.rake +9 -0
  38. data/lib/marsdawn.rb +55 -0
  39. data/marsdawn.gemspec +25 -0
  40. data/spec/_compiled_doc/.gitkeep +0 -0
  41. data/spec/_test_doc/config.yml +10 -0
  42. data/spec/_test_doc/docs01/.index.md +1 -0
  43. data/spec/_test_doc/docs01/.marsdawn.yml +4 -0
  44. data/spec/_test_doc/docs01/010_about.md +4 -0
  45. data/spec/_test_doc/docs01/020_tutorial/.index.md +0 -0
  46. data/spec/_test_doc/docs01/020_tutorial/010_install.md +6 -0
  47. data/spec/_test_doc/docs01/020_tutorial/020_getting_start.md +9 -0
  48. data/spec/_test_doc/docs01/030_reference/1up.md +0 -0
  49. data/spec/_test_doc/docs01/030_reference/each.md +0 -0
  50. data/spec/_test_doc/docs01/030_reference/z-index.md +0 -0
  51. data/spec/_test_doc/docs01/040_appendix.md +3 -0
  52. data/spec/_test_doc/no_config/.gitkeep +0 -0
  53. data/spec/_test_doc/no_key/.marsdawn.yml +3 -0
  54. data/spec/_test_doc/samples/010_sample-document.md +4 -0
  55. data/spec/_test_doc/samples/test_document.md +1 -0
  56. data/spec/_tmp/.gitkeep +0 -0
  57. data/spec/lib/marsdawn/command_spec.rb +144 -0
  58. data/spec/lib/marsdawn/config_spec.rb +41 -0
  59. data/spec/lib/marsdawn/site/page_spec.rb +122 -0
  60. data/spec/lib/marsdawn/site_spec.rb +67 -0
  61. data/spec/lib/marsdawn/source/document_spec.rb +178 -0
  62. data/spec/lib/marsdawn/source/front_matter_spec.rb +55 -0
  63. data/spec/lib/marsdawn/source_spec.rb +40 -0
  64. data/spec/lib/marsdawn/storage/active_record_spec.rb +47 -0
  65. data/spec/lib/marsdawn/storage/file_system_spec.rb +46 -0
  66. data/spec/lib/marsdawn/storage_spec.rb +29 -0
  67. data/spec/lib/marsdawn/util_spec.rb +57 -0
  68. data/spec/spec_helper.rb +12 -0
  69. data/spec/stubs/groonga.rb +17 -0
  70. data/spec/stubs/marsdawn_docs.rb +40 -0
  71. 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