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
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NTI0YWM4Nzg0OWIxYTBiZmM3MjIyZjVjN2U0Yjg3N2FhY2YzZWMzYg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NzAzOTEyZTkwNWNkZjQ2ZjQwMGExOTk1YzNmYTA2MGJlNTNhOTE2OQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MTJiMDlmODk3MGU5MmI1OTRmOWQ2NWE3MjA4YmUyODA5MDIzNGEzODU3OGU3
|
10
|
+
ZjE2Mzg5OTk1MDRjZTY0YWI4NTI2YTVlMDZhN2EzMmVjYzZlNmQ0OTZmYWVi
|
11
|
+
Nzg1YzEzMTc0YWMxYjBlZGY5ZjZiYmU0Y2U1YTRmNWFiMjA5ZDY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OGE0YzBiYmFiYzY1NGEyMzAzOTc5ZjU1Mzg1Y2Y1ZjNjZDJmZjI2MGNhZTJi
|
14
|
+
OWJmOWVhZGQ4ODgwYjljNzc2ZmUzYWZhZGNhZTM0OGVkOTE1MDU3NzhhYjAx
|
15
|
+
Y2FjNmI5MWUwYjNkMDE4OWRlMGVkNTczZGU5ODJiZTk0MDMxMDY=
|
data/.coveralls.yml
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
vendor
|
19
|
+
spec/_compiled_doc/*/
|
20
|
+
spec/_tmp/*/
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 TODO: Write your name
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/nao58/marsdawn.svg?branch=master)](https://travis-ci.org/nao58/marsdawn)
|
2
|
+
[![Coverage Status](https://coveralls.io/repos/nao58/marsdawn/badge.png?branch=master)](https://coveralls.io/r/nao58/marsdawn?branch=master)
|
3
|
+
[![Code Climate](https://codeclimate.com/github/nao58/marsdawn.png)](https://codeclimate.com/github/nao58/marsdawn)
|
4
|
+
|
5
|
+
# Marsdawn
|
6
|
+
|
7
|
+
MarsDawn is simple static document builder and traverser. You can create your own document site easily using this gem.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'marsdawn'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install marsdawn
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
### Create your document source
|
26
|
+
|
27
|
+
Use marsdawn command.
|
28
|
+
|
29
|
+
~~~
|
30
|
+
marsdawn create "My Document"
|
31
|
+
~~~
|
32
|
+
|
33
|
+
Then the document and files will be created like below.
|
34
|
+
|
35
|
+
~~~
|
36
|
+
└── my_document
|
37
|
+
├── .marsdawn.yml
|
38
|
+
└── .index.md
|
39
|
+
~~~
|
40
|
+
|
41
|
+
The file `.marsdawn.yml` includes the information of whole document set. `.index.md` is the top page. You can edit the file to add your content.
|
42
|
+
|
43
|
+
To create new page.
|
44
|
+
|
45
|
+
~~~
|
46
|
+
marsdawn page "About the document"
|
47
|
+
~~~
|
48
|
+
|
49
|
+
~~~
|
50
|
+
└── my_document
|
51
|
+
├── .marsdawn.yml
|
52
|
+
├── .index.md
|
53
|
+
└── 010_about-the-document.md
|
54
|
+
~~~
|
55
|
+
|
56
|
+
To create new directory.
|
57
|
+
|
58
|
+
~~~
|
59
|
+
marsdawn dir "Tutorial"
|
60
|
+
~~~
|
61
|
+
|
62
|
+
~~~
|
63
|
+
└── my_document
|
64
|
+
├── .marsdawn.yml
|
65
|
+
├── .index.md
|
66
|
+
├── 010_about-the-document.md
|
67
|
+
└── 020_tutorial
|
68
|
+
└── .index.md
|
69
|
+
~~~
|
70
|
+
|
71
|
+
### Build the documents
|
72
|
+
|
73
|
+
Setting config file is the easiest way to build the document.
|
74
|
+
|
75
|
+
Create config file names "marsdawn.yml" under "config" directory of your framework.
|
76
|
+
|
77
|
+
~~~config/marsdawn.yml
|
78
|
+
your_document_key:
|
79
|
+
source: path/to/your/source/documents
|
80
|
+
storage:
|
81
|
+
path: path/to/build/directory
|
82
|
+
~~~
|
83
|
+
|
84
|
+
This time we will build the document onto the file system. You can choose RDBMS storage instead.
|
85
|
+
|
86
|
+
Then simply hit the rake command.
|
87
|
+
|
88
|
+
~~~
|
89
|
+
rake marsdawn:build
|
90
|
+
~~~
|
91
|
+
|
92
|
+
### Traverse the documents
|
93
|
+
|
94
|
+
Once you have built the document, you can traverse the document easily from your own site.
|
95
|
+
|
96
|
+
This is a sample controller for Padrino.
|
97
|
+
|
98
|
+
~~~ruby
|
99
|
+
YourWeb::App.controllers :docs do
|
100
|
+
|
101
|
+
get :index, :map => '/docs/*path' do
|
102
|
+
docset = Marsdawn::Site.new(key: 'your_document_key', lang: 'en', version: '1.0.0', base_path: '/docs')
|
103
|
+
@page = docset.page("/#{params[:path].join('/')}")
|
104
|
+
@title = @page.title
|
105
|
+
render 'docs'
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
~~~
|
110
|
+
|
111
|
+
Now you can create your templete for the document page.
|
112
|
+
|
113
|
+
## Contributing
|
114
|
+
|
115
|
+
1. Fork it ( http://github.com/nao58/marsdawn/fork )
|
116
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
117
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
118
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
119
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/marsdawn
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "marsdawn/source"
|
4
|
+
|
5
|
+
class Marsdawn::Builder
|
6
|
+
|
7
|
+
def self.build config
|
8
|
+
raise "No specification for source path." unless config.key?(:source)
|
9
|
+
raise "No specification for storage config." unless config.key?(:storage)
|
10
|
+
source = Marsdawn::Source.new(config[:source])
|
11
|
+
storage = Marsdawn::Storage.get(config[:storage], source.doc_info)
|
12
|
+
build_source source, storage, config[:build_options]
|
13
|
+
create_search_index storage, config[:search] if config.key?(:search)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.build_source source, storage, options
|
17
|
+
options ||= {}
|
18
|
+
kramdown_options = options[:kramdown] || {}
|
19
|
+
storage.prepare
|
20
|
+
storage.set_document_info source.doc_info
|
21
|
+
source.each_contents(kramdown_options) do |uri, content, front_matter, sysinfo|
|
22
|
+
storage.set uri, content, front_matter, sysinfo
|
23
|
+
end
|
24
|
+
storage.finalize
|
25
|
+
rescue => ex
|
26
|
+
storage.clean_up
|
27
|
+
raise ex
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.create_search_index storage, options
|
31
|
+
options ||= {}
|
32
|
+
Marsdawn::Search.create_index storage, options
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Marsdawn
|
6
|
+
class Command
|
7
|
+
attr_accessor :editor, :auto_editor
|
8
|
+
|
9
|
+
class ParamError < Exception; end
|
10
|
+
class RuntimeError < Exception; end
|
11
|
+
|
12
|
+
def self.exec argv
|
13
|
+
raise ParamError.new("No command is specified.") if argv.empty?
|
14
|
+
cmd = new
|
15
|
+
command = argv.shift.to_sym
|
16
|
+
raise ParamError.new("Unknown command '#{command}'.") unless cmd.respond_to?(command)
|
17
|
+
value = (argv.size > 0 ? argv.shift : nil)
|
18
|
+
opts = parse_options(argv)
|
19
|
+
cmd.__send__ command, value, opts
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@editor = 'vim'
|
24
|
+
@auto_editor = true
|
25
|
+
read_dot_marsdawn
|
26
|
+
end
|
27
|
+
|
28
|
+
def create title, opts={}
|
29
|
+
key = (opts.key?(:file) ? opts[:file] : file_namize(title))
|
30
|
+
path = File.expand_path(key)
|
31
|
+
raise "The directory '#{doc_name}' already exists." if File.exists?(path)
|
32
|
+
Dir.mkdir path
|
33
|
+
data = {key: key, title: title, lang: 'en', version: '0.0.1'}
|
34
|
+
dot_file = File.join(path, '.marsdawn.yml')
|
35
|
+
index_file = File.join(path, '.index.md')
|
36
|
+
File.write dot_file, YAML.dump(data)
|
37
|
+
create_page index_file, title, 'title' => title
|
38
|
+
edit_cmd dot_file, index_file if @auto_editor || opts[:edit]
|
39
|
+
end
|
40
|
+
|
41
|
+
def dir title, opts={}
|
42
|
+
dir = (opts.key?(:file) ? opts[:file] : file_namize(title))
|
43
|
+
dir = add_num(dir, opts)
|
44
|
+
path = File.expand_path(dir)
|
45
|
+
Dir.mkdir path
|
46
|
+
index_file = File.join(path, '.index.md')
|
47
|
+
create_page index_file, title, 'title' => title
|
48
|
+
edit_cmd index_file if @auto_editor || opts[:edit]
|
49
|
+
end
|
50
|
+
|
51
|
+
def page title, opts={}
|
52
|
+
file = (opts.key?(:file) ? "#{opts[:file]}.md" : file_namize(title, '.md'))
|
53
|
+
file = File.expand_path(add_num(file, opts))
|
54
|
+
create_page file, title, 'title' => title
|
55
|
+
edit_cmd file if @auto_editor || opts[:edit]
|
56
|
+
end
|
57
|
+
|
58
|
+
def renum step, opts={}
|
59
|
+
step = (step.nil? ? 10 : step.to_i)
|
60
|
+
num = 0
|
61
|
+
list = Dir.glob('*').sort.each_with_object({}) do |item, ret|
|
62
|
+
num += step
|
63
|
+
ret[item] = add_num(item, num: num, step: step)
|
64
|
+
end
|
65
|
+
list.each do |src, dest|
|
66
|
+
FileUtils.mv src, dest unless src == dest
|
67
|
+
end
|
68
|
+
'ls -1'
|
69
|
+
end
|
70
|
+
|
71
|
+
def debug type, opts={}
|
72
|
+
case type
|
73
|
+
when 'options'
|
74
|
+
opts
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def self.parse_options argv
|
80
|
+
opts = {}
|
81
|
+
while argv.size > 0 do
|
82
|
+
key = opt_key(argv.shift)
|
83
|
+
val = true
|
84
|
+
val = argv.shift if argv.size > 0 && !argv.first.start_with?('-')
|
85
|
+
opts[key] = val
|
86
|
+
end
|
87
|
+
opts
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.opt_key switch
|
91
|
+
opt_keys = {
|
92
|
+
'e' => 'edit',
|
93
|
+
'f' => 'file',
|
94
|
+
'n' => 'num',
|
95
|
+
'o' => 'no-num',
|
96
|
+
's' => 'step'
|
97
|
+
}
|
98
|
+
key = switch
|
99
|
+
if key =~ /^--(.+)$/
|
100
|
+
key = $1
|
101
|
+
elsif key =~ /^-(.+)$/
|
102
|
+
key = opt_keys[$1]
|
103
|
+
end
|
104
|
+
raise ParamError.new("Unknown option '#{switch}'.") unless opt_keys.values.include?(key)
|
105
|
+
key.to_sym
|
106
|
+
end
|
107
|
+
|
108
|
+
def read_dot_marsdawn
|
109
|
+
dotfile = File.expand_path("#{ENV['HOME']}/.marsdawn")
|
110
|
+
instance_eval(open(dotfile).read) if File.exists?(dotfile)
|
111
|
+
end
|
112
|
+
|
113
|
+
def file_namize title, ext=''
|
114
|
+
"#{title.downcase.gsub(' ', '-')}#{ext}"
|
115
|
+
end
|
116
|
+
|
117
|
+
def create_page path, title, front_matter={}
|
118
|
+
File.write(path, "#{YAML.dump(front_matter)}---\n\n# #{title}\n\n")
|
119
|
+
end
|
120
|
+
|
121
|
+
def file_num file
|
122
|
+
file =~ /^(\d+)_(.+)$/ ? $1.to_i : 0
|
123
|
+
end
|
124
|
+
|
125
|
+
def file_name file
|
126
|
+
file =~ /^(\d+)_(.+)$/ ? $2 : file
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
def step opts
|
131
|
+
opts.key?(:step) ? opts[:step] : 10
|
132
|
+
end
|
133
|
+
|
134
|
+
def add_num file, opts
|
135
|
+
if opts.key?(:num)
|
136
|
+
num = opts[:num]
|
137
|
+
num = max_num + step(opts) if num.is_a?(TrueClass)
|
138
|
+
num = "000#{num}".slice(-3, 3)
|
139
|
+
file = "#{num}_#{file_name(file)}"
|
140
|
+
end
|
141
|
+
file
|
142
|
+
end
|
143
|
+
|
144
|
+
def max_num
|
145
|
+
list = Dir.glob('*')
|
146
|
+
list.size > 0 ? list.map{|item| file_num(item)}.max : 0
|
147
|
+
end
|
148
|
+
|
149
|
+
def edit_cmd *args
|
150
|
+
"#{@editor} #{args.join(" ")}"
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Marsdawn::Config
|
4
|
+
|
5
|
+
def initialize file='./config/marsdawn.yml'
|
6
|
+
file = File.absolute_path(file)
|
7
|
+
raise "Cannot find a config file for marsdawn." unless File.exists?(file)
|
8
|
+
@config = YAML.load_file(file)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get key, entry, default=nil
|
12
|
+
conf = to_hash(key)
|
13
|
+
conf[entry] = default if !default.nil? && !conf.key?(entry)
|
14
|
+
raise "No entry '#{entry}' in the setting file of marsdawn." unless conf.key?(entry)
|
15
|
+
ret = conf[entry]
|
16
|
+
ret = Marsdawn::Util.hash_symbolize_keys(ret) if ret.kind_of?(Hash)
|
17
|
+
ret
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hash key
|
21
|
+
raise "Cannot find the configuration for '#{key}' in marsdawn.yml." unless @config.key?(key)
|
22
|
+
Marsdawn::Util.hash_symbolize_keys(@config[key])
|
23
|
+
end
|
24
|
+
|
25
|
+
def each
|
26
|
+
@config.keys.each do |key|
|
27
|
+
yield key, to_hash(key)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Marsdawn::Search::Rroonga
|
4
|
+
|
5
|
+
def initialize opts, storage
|
6
|
+
unless Module.const_defined?('Groonga')
|
7
|
+
raise "Gem 'rroonga' should be installed." unless require 'groonga'
|
8
|
+
end
|
9
|
+
raise "The groonga database path should be specified" unless opts.key?(:path)
|
10
|
+
@opts = {
|
11
|
+
index_table: {}
|
12
|
+
}.merge(opts)
|
13
|
+
@storage = storage
|
14
|
+
Groonga::Database.open(opts[:path])
|
15
|
+
@table_prefix = "#{@storage.key}-#{@storage.lang}-#{@storage.version.tr('.','_')}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_tables
|
19
|
+
index_table = {
|
20
|
+
:type => :patricia_trie,
|
21
|
+
:normalizer => :NormalizerAuto,
|
22
|
+
:default_tokenizer => 'TokenBigram'
|
23
|
+
}.merge(@opts[:index_table])
|
24
|
+
Groonga::Schema.create_table(table_name('documents'), :type => :hash) do |tbl|
|
25
|
+
tbl.text('title')
|
26
|
+
tbl.text('content')
|
27
|
+
end
|
28
|
+
Groonga::Schema.create_table(table_name('terms'), index_table) do |tbl|
|
29
|
+
tbl.index(col_name('documents', 'title'))
|
30
|
+
tbl.index(col_name('documents', 'content'))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def drop_tables
|
35
|
+
Groonga::Schema.remove_table(table_name('documents'))
|
36
|
+
Groonga::Schema.remove_table(table_name('terms'))
|
37
|
+
rescue Groonga::Schema::TableNotExists
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_index
|
41
|
+
drop_tables
|
42
|
+
create_tables
|
43
|
+
docs = table('documents')
|
44
|
+
index = @storage.get_document_info[:site_index]
|
45
|
+
index.each do |uri, title|
|
46
|
+
page = @storage.get(uri)
|
47
|
+
docs.add(uri, :title => title, :content => Marsdawn::Util.strip_tags(page[:content]))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def search keyword, opts={}
|
52
|
+
snippet = Groonga::Snippet.new(html_escape: true, default_open_tag: '<strong>', default_close_tag: '</strong>', normalize: true)
|
53
|
+
words = keyword.scan(/(?:\w|"[^"]*")+/).map{|w| w.delete('"')}
|
54
|
+
words.each{|word| snippet.add_keyword(word, opts)}
|
55
|
+
docs = table('documents')
|
56
|
+
docs.select do |rec|
|
57
|
+
expression = nil
|
58
|
+
words.each do |word|
|
59
|
+
sub_exp = (rec.content =~ word)
|
60
|
+
if expression.nil?
|
61
|
+
expression = sub_exp
|
62
|
+
else
|
63
|
+
expression &= sub_exp
|
64
|
+
end
|
65
|
+
end
|
66
|
+
expression
|
67
|
+
end.map{|rec| {uri: rec.key.key, title: rec.title, results: snippet.execute(rec.content)}}
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def table_name name
|
72
|
+
"#{@table_prefix}-#{name}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def table name
|
76
|
+
Groonga[table_name(name)]
|
77
|
+
end
|
78
|
+
|
79
|
+
def col_name table, column
|
80
|
+
"#{table_name(table)}.#{column}"
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Marsdawn::Search
|
4
|
+
|
5
|
+
def self.create_index storage, opts
|
6
|
+
self.get(storage, opts).create_index
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.get storage, opts
|
10
|
+
opts = Marsdawn::Util.hash_symbolize_keys(opts)
|
11
|
+
key = opts[:type]
|
12
|
+
@@base_path ||= File.join(File.dirname(__FILE__), 'search')
|
13
|
+
Marsdawn::Util.adapter(self, class_name, @@base_path).new config, opts
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Marsdawn::Site
|
4
|
+
class Breadcrumb < Hash
|
5
|
+
|
6
|
+
def initialize site, crumb
|
7
|
+
@site = site
|
8
|
+
crumb.each do |path|
|
9
|
+
self[path] = Marsdawn::Site::Link.new(@site, path)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
to_html
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_html
|
18
|
+
words = []
|
19
|
+
if self.size > 0
|
20
|
+
words << '<ul>'
|
21
|
+
self.each do |uri, link|
|
22
|
+
words << "<li>#{link.to_html}</li>"
|
23
|
+
end
|
24
|
+
words << '</ul>'
|
25
|
+
end
|
26
|
+
words.join('')
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Marsdawn::Site
|
4
|
+
class Indexer < Hash
|
5
|
+
|
6
|
+
def initialize site, index={}
|
7
|
+
@site = site
|
8
|
+
@current_page = nil
|
9
|
+
index.each do |uri, title|
|
10
|
+
self[uri] = Marsdawn::Site::Link.new(@site, uri, title)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def current_page path
|
15
|
+
@current_page = path
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
#def parent path
|
20
|
+
# parent_path = "#{File.dirname(path)}/"
|
21
|
+
# self.each_with_object(Marsdawn::Site::Indexer.new(@site).current_page(path)) do |(uri, link), ret|
|
22
|
+
# ret[uri] = link if uri == parent_path
|
23
|
+
# end
|
24
|
+
#end
|
25
|
+
|
26
|
+
def under path
|
27
|
+
base = path + (path == '/' ? '' : '/')
|
28
|
+
self.each_with_object(Marsdawn::Site::Indexer.new(@site).current_page(path)) do |(uri, link), ret|
|
29
|
+
ret[uri] = link if uri.start_with?(base) && uri != base
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def neighbor path
|
34
|
+
level = path.count('/')
|
35
|
+
base = File.dirname(path)
|
36
|
+
base = "#{base}/" if base != '/'
|
37
|
+
self.each_with_object(Marsdawn::Site::Indexer.new(@site).current_page(path)) do |(uri, link), ret|
|
38
|
+
ret[uri] = link if uri.start_with?(base) && uri.count('/') == level && uri != base
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
to_html
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_html options={}
|
47
|
+
if self.size > 0
|
48
|
+
opts = {
|
49
|
+
}.merge(options)
|
50
|
+
create_link_html File.dirname(self.keys.first), opts
|
51
|
+
else
|
52
|
+
""
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def create_link_html path, opts={}
|
58
|
+
base_level = path.count('/')
|
59
|
+
words = []
|
60
|
+
words << '<ul>'
|
61
|
+
self.each do |uri, link|
|
62
|
+
next unless uri.start_with?(path)
|
63
|
+
level = uri.count('/')
|
64
|
+
if base_level < level
|
65
|
+
words.insert -2, create_link_html(uri)
|
66
|
+
elsif base_level == level
|
67
|
+
attr = (uri == @current_page ? ' class="current-page"' : '')
|
68
|
+
words << "<li#{attr}>"
|
69
|
+
words << link.to_html
|
70
|
+
words << '</li>'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
words << '</ul>'
|
74
|
+
words.size > 2 ? words.join('') : ""
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Marsdawn::Site
|
4
|
+
class Link
|
5
|
+
attr_reader :uri, :title, :site, :full_path
|
6
|
+
|
7
|
+
def initialize site, uri, title=nil
|
8
|
+
@uri = uri
|
9
|
+
@title = (title.nil? ? site.page_title(uri) : title)
|
10
|
+
@site = site
|
11
|
+
@full_path = @site.full_path(@uri)
|
12
|
+
end
|
13
|
+
|
14
|
+
def page
|
15
|
+
@site.page @uri
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
to_html
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_html
|
23
|
+
t = CGI.escapeHTML(@title)
|
24
|
+
%!<a href="#{@full_path}" title="#{t}">#{t}</a>!
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|