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.
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