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
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
|
+
[](https://travis-ci.org/nao58/marsdawn)
|
2
|
+
[](https://coveralls.io/r/nao58/marsdawn?branch=master)
|
3
|
+
[](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
|