memorack 0.0.4 → 0.1.0
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 +4 -4
- data/.gitignore +1 -1
- data/HISTORY.md +6 -0
- data/README.md +26 -2
- data/Rakefile +1 -1
- data/VERSION +1 -0
- data/etc/tools/update_revision.rb +14 -0
- data/lib/memorack/builder.rb +171 -0
- data/lib/memorack/cli.rb +53 -0
- data/lib/memorack/core.rb +369 -0
- data/lib/memorack/locales/en.yml +4 -0
- data/lib/memorack/locales/ja.yml +4 -0
- data/lib/memorack/mdmenu.rb +3 -0
- data/lib/memorack/memoapp.rb +14 -350
- data/lib/memorack/template/themes/custom/config.json +2 -0
- data/lib/memorack/themes/basic/config.json +1 -0
- data/lib/memorack/themes/basic/{2-column.scss → css/2-column.scss} +0 -0
- data/lib/memorack/themes/basic/{basic-styles.scss → css/basic-styles.scss} +0 -0
- data/lib/memorack/themes/basic/{styles.scss → css/styles.scss} +0 -0
- data/lib/memorack/themes/basic/index.html +4 -4
- data/lib/memorack/themes/oreilly/{styles.scss → css/styles.scss} +2 -2
- data/lib/memorack/version.rb +23 -3
- data/memorack.gemspec +5 -1
- data/spec/memorack_spec.rb +202 -4
- data/spec/spec_helper.rb +1 -0
- metadata +55 -9
- data/etc/tools/update_version.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86ce4cc169545b6c715a427e966be9ad7a3832a2
|
4
|
+
data.tar.gz: 03e628e48a3764a8ec1dc1636ea6f03b60d51b1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c3d8523641d17d57fee096e9c715330b64f3a85b8f0952606e9e9f6d302d5a16aa1b1eb0908fc4b566b225bf6e632c50ea1e837ac0f737a2033fb21dcafbc0d
|
7
|
+
data.tar.gz: eff59e8755ff34c51c5ad47ab2b92525a95b81628cbb0d1ef2c5c6edeb79ab83eca89313bb764c53b5c19ac7a0d1e62cbb74bdd4471d9b6b8f7e78fc18b9b3b9
|
data/.gitignore
CHANGED
data/HISTORY.md
CHANGED
data/README.md
CHANGED
@@ -23,6 +23,7 @@ Or install it yourself as:
|
|
23
23
|
$ memorack theme THEME # Show theme info
|
24
24
|
$ memorack theme -c THEME # Copy theme
|
25
25
|
$ memorack server PATH # Instant Server
|
26
|
+
$ memorack build [PATH] # Build static site
|
26
27
|
|
27
28
|
Standard startup
|
28
29
|
|
@@ -49,6 +50,14 @@ OS X (Pow + powder)
|
|
49
50
|
* [Pow: Zero-configuration Rack server for Mac OS X](http://pow.cx)
|
50
51
|
* `gem install powder`
|
51
52
|
|
53
|
+
Build static site
|
54
|
+
|
55
|
+
$ memorack create memo
|
56
|
+
$ cd memo
|
57
|
+
(Customizing...)
|
58
|
+
$ memorack build --url http://foo.bar.baz
|
59
|
+
Build 'content/' -> '_site'
|
60
|
+
|
52
61
|
## Directory
|
53
62
|
|
54
63
|
Template
|
@@ -111,10 +120,25 @@ Add code to `index.html`
|
|
111
120
|
<script src="/highlight.js/highlight.pack.js"></script>
|
112
121
|
<script>hljs.initHighlightingOnLoad();</script>
|
113
122
|
|
123
|
+
### org-mode
|
124
|
+
|
125
|
+
Install
|
126
|
+
|
127
|
+
$ gem install org-ruby
|
128
|
+
|
129
|
+
Edit `config.json`
|
130
|
+
|
131
|
+
{
|
132
|
+
"formats": ["markdown", "org"],
|
133
|
+
"requires": ["org-ruby"],
|
134
|
+
...
|
135
|
+
}
|
136
|
+
|
114
137
|
#### mustache variables
|
115
138
|
|
116
139
|
Basic variables -- `{{VAR}}`
|
117
140
|
|
141
|
+
* `site.url`
|
118
142
|
* `title`
|
119
143
|
* `page.title`
|
120
144
|
* `app.name`
|
@@ -131,9 +155,9 @@ Special variables -- `{{{VAR}}}`
|
|
131
155
|
|
132
156
|
* Template comments translate english
|
133
157
|
* Add customizing tips
|
134
|
-
*
|
135
|
-
* Generate HTMLs for static site
|
158
|
+
* More test program
|
136
159
|
* Plugin
|
160
|
+
* Generate EPUB3
|
137
161
|
|
138
162
|
## Contributing
|
139
163
|
|
data/Rakefile
CHANGED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,14 @@
|
|
1
|
+
def update_revision(path = 'REVISION')
|
2
|
+
rev = `git describe --dirty --long 2>/dev/null`.chomp
|
3
|
+
rev = `git describe --dirty --long --tags --always`.chomp if rev.empty?
|
4
|
+
rev.gsub!(/^v([0-9])/, '\1')
|
5
|
+
rev.gsub!(/(^|-)([a-z0-9]+(-dirty)?)$/, '/\2')
|
6
|
+
rev.gsub!(/^([0-9.]+)-0\//, '\1/')
|
7
|
+
|
8
|
+
return if File.exists?(path) && rev == open(path).read.chomp
|
9
|
+
|
10
|
+
open(path, 'w') { |f| f.puts rev }
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
update_revision
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'pathname'
|
6
|
+
require 'rubygems'
|
7
|
+
|
8
|
+
require 'memorack/core'
|
9
|
+
|
10
|
+
module MemoRack
|
11
|
+
class Builder < Core
|
12
|
+
|
13
|
+
DEFAULT_BUILD_OPTIONS = {
|
14
|
+
output: '_site',
|
15
|
+
prefix: '/',
|
16
|
+
suffix: '.html',
|
17
|
+
uri_escape: true,
|
18
|
+
}
|
19
|
+
|
20
|
+
def generate(options = {}, &callback)
|
21
|
+
options = DEFAULT_BUILD_OPTIONS.merge(options)
|
22
|
+
|
23
|
+
url = @site[:url]
|
24
|
+
options[:prefix] = File.join(url, options[:prefix]) unless url.empty?
|
25
|
+
|
26
|
+
output = File.expand_path(options[:output])
|
27
|
+
dir_init(output)
|
28
|
+
|
29
|
+
@contents = contents(options)
|
30
|
+
@templates = Set.new @contents.files.collect { |file| file[:path] }
|
31
|
+
|
32
|
+
# トップページを作成する
|
33
|
+
content_write(:index, '.html', output) { |template|
|
34
|
+
render_with_mustache template, :markdown
|
35
|
+
}
|
36
|
+
|
37
|
+
suffix = options[:suffix]
|
38
|
+
suffix = '/index.html' if ['', '/'].member?(suffix)
|
39
|
+
|
40
|
+
# コンテンツのレンダリングを行う
|
41
|
+
@templates.each { |path|
|
42
|
+
callback.call(path) if callback
|
43
|
+
|
44
|
+
content_write(path, suffix, output) { |template|
|
45
|
+
render_content(template)
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
# テーマの公開ファイルをコピー
|
50
|
+
copy_public(@themes, output, &callback)
|
51
|
+
|
52
|
+
# 静的ファイルをコピーする
|
53
|
+
copy_statics(@root, output, &callback)
|
54
|
+
end
|
55
|
+
|
56
|
+
# ディレクトリを初期化する
|
57
|
+
def dir_init(dir)
|
58
|
+
if Dir.exists?(dir)
|
59
|
+
Dir.glob(File.join(dir, '*'), File::FNM_DOTMATCH) { |path|
|
60
|
+
next if /(^|\/)(\.|\.\.)$/ =~ path
|
61
|
+
|
62
|
+
FileUtils.remove_entry_secure(path)
|
63
|
+
}
|
64
|
+
else
|
65
|
+
FileUtils.mkpath(dir)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# テーマから公開用のファイルを収集する
|
70
|
+
def public_files
|
71
|
+
unless @public_files
|
72
|
+
@public_files = Set.new
|
73
|
+
|
74
|
+
@public.each { |path_info|
|
75
|
+
if path_info[-1] == '/'
|
76
|
+
@themes.each { |theme|
|
77
|
+
if Dir.exists?(File.join(theme, path_info))
|
78
|
+
Dir.chdir(theme) { |dir|
|
79
|
+
@public_files += Dir.glob(File.join(path_info, '**/*'))
|
80
|
+
}
|
81
|
+
end
|
82
|
+
}
|
83
|
+
else
|
84
|
+
@public_files << path_info
|
85
|
+
end
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
@public_files
|
90
|
+
end
|
91
|
+
|
92
|
+
# コンテンツをファイルに出力する
|
93
|
+
def content_write(template, suffix, output)
|
94
|
+
begin
|
95
|
+
content = yield(template)
|
96
|
+
return unless content
|
97
|
+
|
98
|
+
path = template
|
99
|
+
path = path.sub(/\.[^.]*$/, '') if path.kind_of?(String)
|
100
|
+
|
101
|
+
to = path.to_s + suffix
|
102
|
+
to = File.join(output, to)
|
103
|
+
|
104
|
+
FileUtils.mkpath(File.dirname(to))
|
105
|
+
File.write(to, content)
|
106
|
+
rescue
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# ファイルをコピーする
|
111
|
+
def copy_file(path, output, &callback)
|
112
|
+
return if File.directory?(path)
|
113
|
+
return if @templates.include?(path)
|
114
|
+
|
115
|
+
callback.call(path) if callback
|
116
|
+
|
117
|
+
if output.kind_of?(Array)
|
118
|
+
to = File.join(*output)
|
119
|
+
else
|
120
|
+
to = File.join(output, path)
|
121
|
+
end
|
122
|
+
|
123
|
+
FileUtils.mkpath(File.dirname(to))
|
124
|
+
FileUtils.copy_entry(path, to)
|
125
|
+
end
|
126
|
+
|
127
|
+
# 静的ファイルをコピーする
|
128
|
+
def copy_statics(dir, output, &callback)
|
129
|
+
Dir.chdir(dir) { |dir|
|
130
|
+
Dir.glob('**/*') { |path|
|
131
|
+
copy_file(path, output, &callback)
|
132
|
+
}
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
# テーマの公開ファイルをコピーする
|
137
|
+
def copy_public(views, output, &callback)
|
138
|
+
public_files.each { |path_info|
|
139
|
+
callback.call(path_info) if callback
|
140
|
+
|
141
|
+
ext = split_extname(path_info)[1]
|
142
|
+
|
143
|
+
if css_exts.include?(ext)
|
144
|
+
content_write(path_info, '.css', output) { |template|
|
145
|
+
content = render_css(template)
|
146
|
+
}
|
147
|
+
else
|
148
|
+
fullpath = file_search(path_info, {views: views}, nil)
|
149
|
+
copy_file(fullpath, [output, path_info]) if fullpath
|
150
|
+
end
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
#### テンプレート
|
155
|
+
|
156
|
+
# インデックスを作成
|
157
|
+
template :index do
|
158
|
+
begin
|
159
|
+
render :markdown, 'index.md', {views: @themes}
|
160
|
+
rescue
|
161
|
+
''
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# メニューを作成
|
166
|
+
template :menu do
|
167
|
+
@contents.generate(StringIO.new).string
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
end
|
data/lib/memorack/cli.rb
CHANGED
@@ -28,6 +28,7 @@ module MemoRack
|
|
28
28
|
Usage: #{opts.program_name} create [options] PATH
|
29
29
|
#{opts.program_name} theme [options] [THEME]
|
30
30
|
#{opts.program_name} server [options] PATH
|
31
|
+
#{opts.program_name} build [options] [PATH]
|
31
32
|
BANNER
|
32
33
|
|
33
34
|
opts.separator ""
|
@@ -174,6 +175,38 @@ module MemoRack
|
|
174
175
|
abort opts.help if argv.empty?
|
175
176
|
}
|
176
177
|
|
178
|
+
# 静的サイトのビルド
|
179
|
+
define_options(:build, '[options] [PATH]') { |opts, argv, options|
|
180
|
+
default_options = {
|
181
|
+
output: '_site',
|
182
|
+
theme: 'custom',
|
183
|
+
url: '',
|
184
|
+
|
185
|
+
local: false,
|
186
|
+
prettify: false,
|
187
|
+
}
|
188
|
+
|
189
|
+
options.merge!(default_options)
|
190
|
+
|
191
|
+
opts.separator ""
|
192
|
+
opts.on("-o", "--output DIRECTORY", String,
|
193
|
+
t(:output, options)) { |arg| options[:output] = arg }
|
194
|
+
opts.on("-t", "--theme THEME", String,
|
195
|
+
t(:theme, options)) { |arg| options[:theme] = arg }
|
196
|
+
opts.on("--url URL", String,
|
197
|
+
t(:url, options)) { |arg| options[:url] = arg }
|
198
|
+
opts.on("--local",
|
199
|
+
t(:local)) { options[:local] = true }
|
200
|
+
opts.on("--prettify",
|
201
|
+
t(:prettify)) { options[:prettify] = true }
|
202
|
+
opts.on("-h", "--help", t(:help)) { abort opts.help }
|
203
|
+
|
204
|
+
opts.parse!(argv)
|
205
|
+
|
206
|
+
options[:url] = 'file://' + File.expand_path(options[:output]) if options[:local]
|
207
|
+
options[:suffix] = '' if options[:prettify]
|
208
|
+
}
|
209
|
+
|
177
210
|
|
178
211
|
# サブコマンド
|
179
212
|
|
@@ -259,5 +292,25 @@ module MemoRack
|
|
259
292
|
server_options[:app] = app
|
260
293
|
Rack::Server.new(server_options).start
|
261
294
|
end
|
295
|
+
|
296
|
+
# 静的サイトのビルド
|
297
|
+
def memorack_build(options, *argv)
|
298
|
+
path = argv.shift
|
299
|
+
path = 'content/' unless path
|
300
|
+
abort "Directory not exists '#{path}'" unless File.exists?(path)
|
301
|
+
|
302
|
+
require 'memorack/builder'
|
303
|
+
require 'tmpdir'
|
304
|
+
|
305
|
+
Dir.mktmpdir do |tmpdir|
|
306
|
+
site = {}
|
307
|
+
site[:url] = File.join(options[:url], '').gsub(/\/$/, '') if options[:url]
|
308
|
+
|
309
|
+
builder = MemoRack::Builder.new(theme: options[:theme], root: path, tmpdir: tmpdir, site: site)
|
310
|
+
builder.generate(options)
|
311
|
+
end
|
312
|
+
|
313
|
+
puts "Build '#{path}' -> '#{options[:output]}'"
|
314
|
+
end
|
262
315
|
end
|
263
316
|
end
|
@@ -0,0 +1,369 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'rubygems'
|
5
|
+
|
6
|
+
require 'memorack/tilt-mustache'
|
7
|
+
require 'memorack/locals'
|
8
|
+
require 'memorack/mdmenu'
|
9
|
+
|
10
|
+
module MemoRack
|
11
|
+
class Core
|
12
|
+
attr_reader :themes, :options_chain
|
13
|
+
|
14
|
+
DEFAULT_APP_OPTIONS = {
|
15
|
+
root: 'content/',
|
16
|
+
themes_folder: 'themes/',
|
17
|
+
tmpdir: 'tmp/',
|
18
|
+
theme: 'basic',
|
19
|
+
markdown: 'redcarpet',
|
20
|
+
formats: ['markdown'],
|
21
|
+
css: nil,
|
22
|
+
suffix: '',
|
23
|
+
public: [],
|
24
|
+
site: {},
|
25
|
+
requires: [],
|
26
|
+
directory_watcher: false
|
27
|
+
}
|
28
|
+
|
29
|
+
# テンプレートエンジンのオプション
|
30
|
+
DEFAULT_TEMPLATE_OPTIONS = {
|
31
|
+
tables: true
|
32
|
+
}
|
33
|
+
|
34
|
+
# テンプレートで使用するローカル変数の初期値
|
35
|
+
DEFAULT_LOCALS = {
|
36
|
+
title: 'memo'
|
37
|
+
}
|
38
|
+
|
39
|
+
DEFAULT_OPTIONS = DEFAULT_APP_OPTIONS.merge(DEFAULT_TEMPLATE_OPTIONS).merge(DEFAULT_LOCALS)
|
40
|
+
|
41
|
+
def initialize(options={})
|
42
|
+
options = DEFAULT_OPTIONS.merge(to_sym_keys(options))
|
43
|
+
|
44
|
+
@themes_folders = [options[:themes_folder], File.expand_path('../themes/', __FILE__)]
|
45
|
+
read_config(options[:theme], options)
|
46
|
+
read_config(DEFAULT_APP_OPTIONS[:theme], options) if @themes.empty?
|
47
|
+
|
48
|
+
@options = options
|
49
|
+
|
50
|
+
# DEFAULT_APP_OPTIONS に含まれるキーをすべてインスタンス変数に登録する
|
51
|
+
DEFAULT_APP_OPTIONS.each { |key, item|
|
52
|
+
instance_variable_set("@#{key}".to_sym, options[key])
|
53
|
+
|
54
|
+
# @options からテンプレートで使わないものを削除
|
55
|
+
@options.delete(key)
|
56
|
+
}
|
57
|
+
|
58
|
+
@requires.each { |lib| require lib }
|
59
|
+
@locals = default_locals(@options)
|
60
|
+
|
61
|
+
use_engine(@markdown)
|
62
|
+
end
|
63
|
+
|
64
|
+
# テーマのパスを取得する
|
65
|
+
def theme_path(theme)
|
66
|
+
return nil unless theme
|
67
|
+
|
68
|
+
@themes_folders.each { |folder|
|
69
|
+
path = theme && File.join(folder, theme)
|
70
|
+
return path if File.exists?(path) && FileTest::directory?(path)
|
71
|
+
}
|
72
|
+
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
|
76
|
+
# デフォルトの locals を生成する
|
77
|
+
def default_locals(locals = {})
|
78
|
+
locals = Locals[locals]
|
79
|
+
|
80
|
+
locals[:site] ||= @site
|
81
|
+
|
82
|
+
locals[:app] ||= Locals[]
|
83
|
+
locals[:app][:name] ||= MemoRack::name
|
84
|
+
locals[:app][:version] ||= MemoRack::VERSION
|
85
|
+
locals[:app][:url] ||= MemoRack::HOMEPAGE
|
86
|
+
|
87
|
+
locals.define_key(:__menu__) { |hash, key|
|
88
|
+
@menu = nil unless @directory_watcher # ファイル監視していない場合はメニューを初期化
|
89
|
+
@menu ||= render :markdown, :menu, @options
|
90
|
+
}
|
91
|
+
|
92
|
+
locals
|
93
|
+
end
|
94
|
+
|
95
|
+
# 設定ファイルを読込む
|
96
|
+
def read_config(theme, options = {})
|
97
|
+
@themes ||= []
|
98
|
+
@options_chain = []
|
99
|
+
@theme_chain = []
|
100
|
+
|
101
|
+
begin
|
102
|
+
require 'json'
|
103
|
+
|
104
|
+
while theme
|
105
|
+
dir = theme_path(theme)
|
106
|
+
break unless dir
|
107
|
+
break if @themes.member?(dir)
|
108
|
+
|
109
|
+
# テーマ・チェインに追加
|
110
|
+
@themes << File.join(dir, '')
|
111
|
+
|
112
|
+
# config の読込み
|
113
|
+
path = File.join(dir, 'config.json')
|
114
|
+
break unless File.readable?(path)
|
115
|
+
|
116
|
+
data = File.read(path)
|
117
|
+
@options_chain << to_sym_keys(JSON.parse(data))
|
118
|
+
|
119
|
+
theme = @options_chain.last[:theme]
|
120
|
+
end
|
121
|
+
rescue
|
122
|
+
end
|
123
|
+
|
124
|
+
# オプションをマージ
|
125
|
+
@options_chain.reverse.each { |opts| options.merge!(opts) }
|
126
|
+
options
|
127
|
+
end
|
128
|
+
|
129
|
+
# テンプレートエンジンを使用できるようにする
|
130
|
+
def use_engine(engine)
|
131
|
+
require engine if engine
|
132
|
+
|
133
|
+
# Tilt で Redcarpet 2.x を使うためのおまじない
|
134
|
+
Object.send(:remove_const, :RedcarpetCompat) if defined?(RedcarpetCompat) == 'constant'
|
135
|
+
end
|
136
|
+
|
137
|
+
# css の拡張子リストを作成する
|
138
|
+
def css_exts
|
139
|
+
@css_exts ||= Set.new ['css', *@css]
|
140
|
+
end
|
141
|
+
|
142
|
+
# ファイルを探す
|
143
|
+
def file_search(template, options = {}, exts = enable_exts)
|
144
|
+
options = {views: @root}.merge(options)
|
145
|
+
|
146
|
+
if options[:views].kind_of?(Array)
|
147
|
+
err = nil
|
148
|
+
|
149
|
+
options[:views].each { |views|
|
150
|
+
options[:views] = views
|
151
|
+
|
152
|
+
begin
|
153
|
+
path = file_search(template, options, exts)
|
154
|
+
return path if path
|
155
|
+
rescue Errno::ENOENT => e
|
156
|
+
err = e
|
157
|
+
end
|
158
|
+
}
|
159
|
+
|
160
|
+
raise err if err
|
161
|
+
return nil
|
162
|
+
end
|
163
|
+
|
164
|
+
if exts
|
165
|
+
exts.each { |ext|
|
166
|
+
path = File.join(options[:views], "#{template}.#{ext}")
|
167
|
+
return path if File.exists?(path)
|
168
|
+
}
|
169
|
+
else
|
170
|
+
path = File.join(options[:views], template)
|
171
|
+
return path if File.exists?(path)
|
172
|
+
end
|
173
|
+
|
174
|
+
return nil
|
175
|
+
end
|
176
|
+
|
177
|
+
# テンプレートエンジンで render する
|
178
|
+
def render(engine, template, options = {}, locals = {})
|
179
|
+
options = {views: @root}.merge(options)
|
180
|
+
|
181
|
+
if template.kind_of?(Pathname)
|
182
|
+
path = template
|
183
|
+
elsif options[:views].kind_of?(Array)
|
184
|
+
err = nil
|
185
|
+
|
186
|
+
options[:views].each { |views|
|
187
|
+
options[:views] = views
|
188
|
+
|
189
|
+
begin
|
190
|
+
return render(engine, template, options, locals)
|
191
|
+
rescue Errno::ENOENT => e
|
192
|
+
err = e
|
193
|
+
end
|
194
|
+
}
|
195
|
+
|
196
|
+
raise err
|
197
|
+
else
|
198
|
+
fname = template.kind_of?(String) ? template : "#{template}.#{engine}"
|
199
|
+
path = File.join(options[:views], fname)
|
200
|
+
end
|
201
|
+
|
202
|
+
engine = Tilt.new(File.join(File.dirname(path), ".#{engine}"), options) {
|
203
|
+
method = MemoApp.template_method(template)
|
204
|
+
|
205
|
+
if method && respond_to?(method)
|
206
|
+
data = send(method)
|
207
|
+
else
|
208
|
+
data = File.binread(path)
|
209
|
+
data.force_encoding('UTF-8')
|
210
|
+
end
|
211
|
+
|
212
|
+
data
|
213
|
+
}
|
214
|
+
engine.render(options, locals).force_encoding('UTF-8')
|
215
|
+
end
|
216
|
+
|
217
|
+
# レイアウトに mustache を適用してテンプレートエンジンでレンダリングする
|
218
|
+
def render_with_mustache(template, engine = :markdown, options = {}, locals = {})
|
219
|
+
begin
|
220
|
+
mustache_templ = options[:mustache] || 'index.html'
|
221
|
+
|
222
|
+
options = @options.merge(options)
|
223
|
+
locals = @locals.merge(locals)
|
224
|
+
|
225
|
+
locals.define_key(:__content__) { |hash, key|
|
226
|
+
if engine
|
227
|
+
render engine, template, options
|
228
|
+
else
|
229
|
+
template
|
230
|
+
end
|
231
|
+
}
|
232
|
+
|
233
|
+
locals[:content] = true unless template == :index
|
234
|
+
locals[:page] = page = Locals[locals[:page] || {}]
|
235
|
+
|
236
|
+
page.define_key(:name) { |hash, key|
|
237
|
+
unless template == :index
|
238
|
+
fname = locals[:path_info]
|
239
|
+
fname ||= template.to_s.force_encoding('UTF-8')
|
240
|
+
File.basename(fname)
|
241
|
+
end
|
242
|
+
}
|
243
|
+
|
244
|
+
page.define_key(:title) { |hash, key|
|
245
|
+
page_title = home_title = locals[:title]
|
246
|
+
page_name = hash[:name]
|
247
|
+
page_title = "#{page_name} | #{home_title}" if page_name
|
248
|
+
|
249
|
+
page_title
|
250
|
+
}
|
251
|
+
|
252
|
+
render :mustache, mustache_templ, {views: @themes}, locals
|
253
|
+
rescue => e
|
254
|
+
e.to_s
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# コンテンツをレンダリングする
|
259
|
+
def render_content(path_info, locals = {})
|
260
|
+
path, ext = split_extname(path_info)
|
261
|
+
|
262
|
+
if @suffix == ''
|
263
|
+
fullpath = file_search(path_info, @options)
|
264
|
+
|
265
|
+
if fullpath
|
266
|
+
path = path_info
|
267
|
+
ext = split_extname(fullpath)[1]
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
return nil unless ext && Tilt.registered?(ext)
|
272
|
+
|
273
|
+
template = fullpath ? Pathname.new(fullpath) : path.to_sym
|
274
|
+
content = render_with_mustache template, ext, {}, locals
|
275
|
+
end
|
276
|
+
|
277
|
+
# CSSをレンダリングする
|
278
|
+
def render_css(path_info, locals = {})
|
279
|
+
return unless @css
|
280
|
+
|
281
|
+
path, = split_extname(path_info)
|
282
|
+
options = {views: @themes}
|
283
|
+
|
284
|
+
fullpath = file_search(path, options, css_exts)
|
285
|
+
return nil unless fullpath
|
286
|
+
|
287
|
+
ext = split_extname(fullpath)[1]
|
288
|
+
|
289
|
+
case ext
|
290
|
+
when 'css'
|
291
|
+
return File.binread(fullpath)
|
292
|
+
when 'scss', 'sass'
|
293
|
+
options[:cache_location] = File.expand_path('sass-cache', @tmpdir)
|
294
|
+
end
|
295
|
+
|
296
|
+
render ext, Pathname.new(fullpath), options, locals
|
297
|
+
end
|
298
|
+
|
299
|
+
# 拡張子を取出す
|
300
|
+
def split_extname(path)
|
301
|
+
return [$1, $2] if /^(.+)\.([^.]+)/ =~ path
|
302
|
+
|
303
|
+
[path]
|
304
|
+
end
|
305
|
+
|
306
|
+
# キーをシンボルに変換する
|
307
|
+
def to_sym_keys(hash)
|
308
|
+
hash.inject({}) { |memo, entry|
|
309
|
+
key, value = entry
|
310
|
+
value = to_sym_keys(value) if value.kind_of?(Hash)
|
311
|
+
|
312
|
+
memo[key.to_sym] = value
|
313
|
+
|
314
|
+
memo
|
315
|
+
}
|
316
|
+
end
|
317
|
+
|
318
|
+
# Tilt に登録されている拡張子を集める
|
319
|
+
def extnames(extname)
|
320
|
+
klass = Tilt[extname]
|
321
|
+
Tilt.mappings.select { |key, value| value.member?(klass) }.collect { |key, value| key }
|
322
|
+
end
|
323
|
+
|
324
|
+
# 対応フォーマットを取得する
|
325
|
+
def collect_formats
|
326
|
+
unless @collect_formats
|
327
|
+
@collect_formats = {}
|
328
|
+
|
329
|
+
@formats.each { |item|
|
330
|
+
if item.kind_of?(Array)
|
331
|
+
@collect_formats[item.first] = item
|
332
|
+
elsif item.kind_of?(Hash)
|
333
|
+
@collect_formats.merge!(item)
|
334
|
+
else
|
335
|
+
@collect_formats[item] = extnames(item)
|
336
|
+
end
|
337
|
+
}
|
338
|
+
end
|
339
|
+
|
340
|
+
@collect_formats
|
341
|
+
end
|
342
|
+
|
343
|
+
# 対応している拡張子
|
344
|
+
def enable_exts
|
345
|
+
@enable_exts ||= collect_formats.values.flatten
|
346
|
+
end
|
347
|
+
|
348
|
+
# コンテンツファイルの収集する
|
349
|
+
def contents(options = {})
|
350
|
+
default_options = {prefix: '/', suffix: @suffix, uri_escape: true, formats: collect_formats}
|
351
|
+
options = default_options.merge(options)
|
352
|
+
|
353
|
+
mdmenu = MdMenu.new(options)
|
354
|
+
Dir.chdir(@root) { |path| mdmenu.collection('.') }
|
355
|
+
mdmenu
|
356
|
+
end
|
357
|
+
|
358
|
+
# テンプレート名
|
359
|
+
def self.template_method(name)
|
360
|
+
name.kind_of?(Symbol) && "template_#{name}".to_sym
|
361
|
+
end
|
362
|
+
|
363
|
+
# テンプレートを作成する
|
364
|
+
def self.template(name, &block)
|
365
|
+
define_method(self.template_method(name), &block)
|
366
|
+
end
|
367
|
+
|
368
|
+
end
|
369
|
+
end
|