memorack 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/memorack/locales/en.yml
CHANGED
@@ -5,3 +5,7 @@ en:
|
|
5
5
|
dir: "Theme directory (default: %{dir})"
|
6
6
|
port: "use PORT (default: %{Port})"
|
7
7
|
theme: "use THEME (default: %{theme})"
|
8
|
+
output: "Output directory (default: %{output})"
|
9
|
+
url: "Site URL (default: %{url})"
|
10
|
+
local: "Site URL is output directory"
|
11
|
+
prettify: "prettify URL"
|
data/lib/memorack/locales/ja.yml
CHANGED
data/lib/memorack/mdmenu.rb
CHANGED
@@ -18,6 +18,8 @@ class MdMenu
|
|
18
18
|
'html' => ['html', 'htm']
|
19
19
|
}
|
20
20
|
|
21
|
+
attr_reader :files
|
22
|
+
|
21
23
|
def initialize(config)
|
22
24
|
@config = config
|
23
25
|
@file = config[:file]
|
@@ -104,6 +106,7 @@ class MdMenu
|
|
104
106
|
|
105
107
|
prefix = @config[:prefix]
|
106
108
|
|
109
|
+
d = File.join(d, '')
|
107
110
|
d.gsub!(/^#{prefix}/, '') if prefix
|
108
111
|
ds = d.scan(/[^\/]+/)
|
109
112
|
ds.delete('.')
|
data/lib/memorack/memoapp.rb
CHANGED
@@ -5,59 +5,15 @@ require 'rubygems'
|
|
5
5
|
require 'rack'
|
6
6
|
require 'uri'
|
7
7
|
|
8
|
-
require 'memorack/
|
9
|
-
require 'memorack/mdmenu'
|
10
|
-
require 'memorack/locals'
|
8
|
+
require 'memorack/core'
|
11
9
|
|
12
10
|
module MemoRack
|
13
|
-
class MemoApp
|
14
|
-
attr_reader :themes, :options_chain
|
15
|
-
|
16
|
-
DEFAULT_APP_OPTIONS = {
|
17
|
-
root: 'content/',
|
18
|
-
themes_folder: 'themes/',
|
19
|
-
tmpdir: 'tmp/',
|
20
|
-
theme: 'basic',
|
21
|
-
markdown: 'redcarpet',
|
22
|
-
formats: ['markdown'],
|
23
|
-
css: nil,
|
24
|
-
suffix: '',
|
25
|
-
directory_watcher: false
|
26
|
-
}
|
27
|
-
|
28
|
-
# テンプレートエンジンのオプション
|
29
|
-
DEFAULT_TEMPLATE_OPTIONS = {
|
30
|
-
tables: true
|
31
|
-
}
|
32
|
-
|
33
|
-
# テンプレートで使用するローカル変数の初期値
|
34
|
-
DEFAULT_LOCALS = {
|
35
|
-
title: 'memo'
|
36
|
-
}
|
37
|
-
|
38
|
-
DEFAULT_OPTIONS = DEFAULT_APP_OPTIONS.merge(DEFAULT_TEMPLATE_OPTIONS).merge(DEFAULT_LOCALS)
|
11
|
+
class MemoApp < Core
|
39
12
|
|
40
13
|
def initialize(app, options={})
|
41
|
-
|
42
|
-
|
43
|
-
@themes_folders = [options[:themes_folder], File.expand_path('../themes/', __FILE__)]
|
44
|
-
read_config(options[:theme], options)
|
45
|
-
read_config(DEFAULT_APP_OPTIONS[:theme], options) if @themes.empty?
|
14
|
+
super(options)
|
46
15
|
|
47
16
|
@app = app
|
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
|
-
@locals = default_locals(@options)
|
59
|
-
|
60
|
-
use_engine(@markdown)
|
61
17
|
define_statics(@root, *@themes)
|
62
18
|
|
63
19
|
# ファイル監視を行う
|
@@ -78,12 +34,13 @@ module MemoRack
|
|
78
34
|
|
79
35
|
begin
|
80
36
|
content_type = 'text/css'
|
81
|
-
content = render_css(
|
37
|
+
content = render_css(path_info)
|
82
38
|
rescue Errno::ENOENT => e
|
83
39
|
return error(env, 404)
|
84
40
|
end
|
85
41
|
else
|
86
|
-
|
42
|
+
locals = {env: env, path_info: path_info}
|
43
|
+
content = render_content(path_info, locals)
|
87
44
|
end
|
88
45
|
|
89
46
|
return [200, {'Content-Type' => content_type}, [content.to_s]] if content
|
@@ -93,6 +50,13 @@ module MemoRack
|
|
93
50
|
}
|
94
51
|
end
|
95
52
|
|
53
|
+
# 静的ファイルの参照先を定義する
|
54
|
+
def define_statics(*args)
|
55
|
+
@statics = [] unless @statics
|
56
|
+
|
57
|
+
@statics |= args.collect { |root| Rack::File.new(root) }
|
58
|
+
end
|
59
|
+
|
96
60
|
# PATH_INFO を unescape して取出す
|
97
61
|
def unescape_path_info(env)
|
98
62
|
path_info = URI.unescape(env['PATH_INFO'])
|
@@ -105,84 +69,6 @@ module MemoRack
|
|
105
69
|
[code, {'Content-Type' => 'text/html', 'Location' => url}, ['Redirect: ', url]]
|
106
70
|
end
|
107
71
|
|
108
|
-
# テンプレートエンジンを使用できるようにする
|
109
|
-
def use_engine(engine)
|
110
|
-
require engine if engine
|
111
|
-
|
112
|
-
# Tilt で Redcarpet 2.x を使うためのおまじない
|
113
|
-
Object.send(:remove_const, :RedcarpetCompat) if defined?(RedcarpetCompat) == 'constant'
|
114
|
-
end
|
115
|
-
|
116
|
-
# テーマのパスを取得する
|
117
|
-
def theme_path(theme)
|
118
|
-
return nil unless theme
|
119
|
-
|
120
|
-
@themes_folders.each { |folder|
|
121
|
-
path = theme && File.join(folder, theme)
|
122
|
-
return path if File.exists?(path) && FileTest::directory?(path)
|
123
|
-
}
|
124
|
-
|
125
|
-
nil
|
126
|
-
end
|
127
|
-
|
128
|
-
# デフォルトの locals を生成する
|
129
|
-
def default_locals(locals = {})
|
130
|
-
locals = Locals[locals]
|
131
|
-
|
132
|
-
locals[:app] ||= Locals[]
|
133
|
-
locals[:app][:name] ||= MemoRack::name
|
134
|
-
locals[:app][:version] ||= MemoRack::VERSION
|
135
|
-
locals[:app][:url] ||= MemoRack::HOMEPAGE
|
136
|
-
|
137
|
-
locals.define_key(:__menu__) { |hash, key|
|
138
|
-
@menu = nil unless @directory_watcher # ファイル監視していない場合はメニューを初期化
|
139
|
-
@menu ||= render :markdown, :menu, @options
|
140
|
-
}
|
141
|
-
|
142
|
-
locals
|
143
|
-
end
|
144
|
-
|
145
|
-
# 設定ファイルを読込む
|
146
|
-
def read_config(theme, options = {})
|
147
|
-
@themes ||= []
|
148
|
-
@options_chain = []
|
149
|
-
@theme_chain = []
|
150
|
-
|
151
|
-
begin
|
152
|
-
require 'json'
|
153
|
-
|
154
|
-
while theme
|
155
|
-
dir = theme_path(theme)
|
156
|
-
break unless dir
|
157
|
-
break if @themes.member?(dir)
|
158
|
-
|
159
|
-
# テーマ・チェインに追加
|
160
|
-
@themes << File.join(dir, '')
|
161
|
-
|
162
|
-
# config の読込み
|
163
|
-
path = File.join(dir, 'config.json')
|
164
|
-
break unless File.readable?(path)
|
165
|
-
|
166
|
-
data = File.read(path)
|
167
|
-
@options_chain << to_sym_keys(JSON.parse(data))
|
168
|
-
|
169
|
-
theme = @options_chain.last[:theme]
|
170
|
-
end
|
171
|
-
rescue
|
172
|
-
end
|
173
|
-
|
174
|
-
# オプションをマージ
|
175
|
-
@options_chain.reverse.each { |opts| options.merge!(opts) }
|
176
|
-
options
|
177
|
-
end
|
178
|
-
|
179
|
-
# 静的ファイルの参照先を定義する
|
180
|
-
def define_statics(*args)
|
181
|
-
@statics = [] unless @statics
|
182
|
-
|
183
|
-
@statics |= args.collect { |root| Rack::File.new(root) }
|
184
|
-
end
|
185
|
-
|
186
72
|
# 次のアプリにパスする
|
187
73
|
def pass(env, apps = @statics + [@app])
|
188
74
|
apps.each { |app|
|
@@ -248,226 +134,6 @@ module MemoRack
|
|
248
134
|
dw.start
|
249
135
|
end
|
250
136
|
|
251
|
-
# ファイルを探す
|
252
|
-
def file_search(template, options = {}, exts = enable_exts)
|
253
|
-
options = {views: @root}.merge(options)
|
254
|
-
|
255
|
-
if options[:views].kind_of?(Array)
|
256
|
-
err = nil
|
257
|
-
|
258
|
-
options[:views].each { |views|
|
259
|
-
options[:views] = views
|
260
|
-
|
261
|
-
begin
|
262
|
-
path = file_search(template, options, exts)
|
263
|
-
return path if path
|
264
|
-
rescue Errno::ENOENT => e
|
265
|
-
err = e
|
266
|
-
end
|
267
|
-
}
|
268
|
-
|
269
|
-
raise err if err
|
270
|
-
return nil
|
271
|
-
end
|
272
|
-
|
273
|
-
exts.each { |ext|
|
274
|
-
path = File.join(options[:views], "#{template}.#{ext}")
|
275
|
-
return path if File.exists?(path)
|
276
|
-
}
|
277
|
-
|
278
|
-
return nil
|
279
|
-
end
|
280
|
-
|
281
|
-
# テンプレートエンジンで render する
|
282
|
-
def render(engine, template, options = {}, locals = {})
|
283
|
-
options = {views: @root}.merge(options)
|
284
|
-
|
285
|
-
if template.kind_of?(Pathname)
|
286
|
-
path = template
|
287
|
-
elsif options[:views].kind_of?(Array)
|
288
|
-
err = nil
|
289
|
-
|
290
|
-
options[:views].each { |views|
|
291
|
-
options[:views] = views
|
292
|
-
|
293
|
-
begin
|
294
|
-
return render(engine, template, options, locals)
|
295
|
-
rescue Errno::ENOENT => e
|
296
|
-
err = e
|
297
|
-
end
|
298
|
-
}
|
299
|
-
|
300
|
-
raise err
|
301
|
-
else
|
302
|
-
fname = template.kind_of?(String) ? template : "#{template}.#{engine}"
|
303
|
-
path = File.join(options[:views], fname)
|
304
|
-
end
|
305
|
-
|
306
|
-
engine = Tilt.new(File.join(File.dirname(path), ".#{engine}"), options) {
|
307
|
-
method = MemoApp.template_method(template)
|
308
|
-
|
309
|
-
if method && respond_to?(method)
|
310
|
-
data = send(method)
|
311
|
-
else
|
312
|
-
data = File.binread(path)
|
313
|
-
data.force_encoding('UTF-8')
|
314
|
-
end
|
315
|
-
|
316
|
-
data
|
317
|
-
}
|
318
|
-
engine.render(options, locals).force_encoding('UTF-8')
|
319
|
-
end
|
320
|
-
|
321
|
-
# レイアウトに mustache を適用してテンプレートエンジンでレンダリングする
|
322
|
-
def render_with_mustache(template, engine = :markdown, options = {}, locals = {})
|
323
|
-
begin
|
324
|
-
mustache_templ = options[:mustache] || 'index.html'
|
325
|
-
|
326
|
-
options = @options.merge(options)
|
327
|
-
locals = @locals.merge(locals)
|
328
|
-
|
329
|
-
locals.define_key(:__content__) { |hash, key|
|
330
|
-
if engine
|
331
|
-
render engine, template, options
|
332
|
-
else
|
333
|
-
template
|
334
|
-
end
|
335
|
-
}
|
336
|
-
|
337
|
-
locals[:content] = true unless template == :index
|
338
|
-
locals[:page] = page = Locals[locals[:page] || {}]
|
339
|
-
|
340
|
-
page.define_key(:name) { |hash, key|
|
341
|
-
unless template == :index
|
342
|
-
fname = locals[:path_info]
|
343
|
-
fname ||= template.to_s.force_encoding('UTF-8')
|
344
|
-
File.basename(fname)
|
345
|
-
end
|
346
|
-
}
|
347
|
-
|
348
|
-
page.define_key(:title) { |hash, key|
|
349
|
-
page_title = home_title = locals[:title]
|
350
|
-
page_name = hash[:name]
|
351
|
-
page_title = "#{page_name} | #{home_title}" if page_name
|
352
|
-
|
353
|
-
page_title
|
354
|
-
}
|
355
|
-
|
356
|
-
render :mustache, mustache_templ, {views: @themes}, locals
|
357
|
-
rescue => e
|
358
|
-
e.to_s
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
# コンテンツをレンダリングする
|
363
|
-
def render_content(env, path_info)
|
364
|
-
path, ext = split_extname(path_info)
|
365
|
-
|
366
|
-
if @suffix == ''
|
367
|
-
fullpath = file_search(path_info, @options)
|
368
|
-
|
369
|
-
if fullpath
|
370
|
-
path = path_info
|
371
|
-
ext = split_extname(fullpath)[1]
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
return nil unless ext && Tilt.registered?(ext)
|
376
|
-
|
377
|
-
req = Rack::Request.new(env)
|
378
|
-
query = Rack::Utils.parse_query(req.query_string)
|
379
|
-
locals = {env: env, path_info: path_info}
|
380
|
-
|
381
|
-
if query.has_key?('edit')
|
382
|
-
fullpath = File.expand_path(File.join(@root, "#{path}.#{ext}")) unless fullpath
|
383
|
-
|
384
|
-
# @attention リダイレクトはうまく動作しない
|
385
|
-
#
|
386
|
-
# redirect_url = 'file://' + File.expand_path(File.join(@root, req.path_info))
|
387
|
-
# return redirect(redirect_url, 302) if File.exists?(fullpath)
|
388
|
-
end
|
389
|
-
|
390
|
-
template = fullpath ? Pathname.new(fullpath) : path.to_sym
|
391
|
-
content = render_with_mustache template, ext, {}, locals
|
392
|
-
end
|
393
|
-
|
394
|
-
# CSSをレンダリングする
|
395
|
-
def render_css(env, path_info)
|
396
|
-
return unless @css
|
397
|
-
|
398
|
-
exts = @css
|
399
|
-
exts = [exts] unless exts.kind_of?(Array)
|
400
|
-
path, = split_extname(path_info)
|
401
|
-
options = {views: @themes}
|
402
|
-
|
403
|
-
fullpath = file_search(path, options, exts)
|
404
|
-
return nil unless fullpath
|
405
|
-
|
406
|
-
ext = split_extname(fullpath)[1]
|
407
|
-
|
408
|
-
case ext
|
409
|
-
when 'scss', 'sass'
|
410
|
-
options[:cache_location] = File.expand_path('sass-cache', @tmpdir)
|
411
|
-
end
|
412
|
-
|
413
|
-
render ext, Pathname.new(fullpath), options
|
414
|
-
end
|
415
|
-
|
416
|
-
# 拡張子を取出す
|
417
|
-
def split_extname(path)
|
418
|
-
return [$1, $2] if /^(.+)\.([^.]+)/ =~ path
|
419
|
-
|
420
|
-
[path]
|
421
|
-
end
|
422
|
-
|
423
|
-
# キーをシンボルに変換する
|
424
|
-
def to_sym_keys(hash)
|
425
|
-
hash.inject({}) { |memo, entry|
|
426
|
-
key, value = entry
|
427
|
-
memo[key.to_sym] = value
|
428
|
-
memo
|
429
|
-
}
|
430
|
-
end
|
431
|
-
|
432
|
-
# Tilt に登録されている拡張子を集める
|
433
|
-
def extnames(extname)
|
434
|
-
klass = Tilt[extname]
|
435
|
-
Tilt.mappings.select { |key, value| value.member?(klass) }.collect { |key, value| key }
|
436
|
-
end
|
437
|
-
|
438
|
-
# 対応フォーマットを取得する
|
439
|
-
def collect_formats
|
440
|
-
unless @collect_formats
|
441
|
-
@collect_formats = {}
|
442
|
-
|
443
|
-
@formats.each { |item|
|
444
|
-
if item.kind_of?(Array)
|
445
|
-
@collect_formats[item.first] = item
|
446
|
-
elsif item.kind_of?(Hash)
|
447
|
-
@collect_formats.merge!(item)
|
448
|
-
else
|
449
|
-
@collect_formats[item] = extnames(item)
|
450
|
-
end
|
451
|
-
}
|
452
|
-
end
|
453
|
-
|
454
|
-
@collect_formats
|
455
|
-
end
|
456
|
-
|
457
|
-
# 対応している拡張子
|
458
|
-
def enable_exts
|
459
|
-
@enable_exts ||= collect_formats.values.flatten
|
460
|
-
end
|
461
|
-
|
462
|
-
# テンプレート名
|
463
|
-
def self.template_method(name)
|
464
|
-
name.kind_of?(Symbol) && "template_#{name}".to_sym
|
465
|
-
end
|
466
|
-
|
467
|
-
# テンプレートを作成する
|
468
|
-
def self.template(name, &block)
|
469
|
-
define_method(self.template_method(name), &block)
|
470
|
-
end
|
471
137
|
|
472
138
|
#### テンプレート
|
473
139
|
|
@@ -482,9 +148,7 @@ module MemoRack
|
|
482
148
|
|
483
149
|
# メニューを作成
|
484
150
|
template :menu do
|
485
|
-
|
486
|
-
Dir.chdir(@root) { |path| mdmenu.collection('.') }
|
487
|
-
mdmenu.generate(StringIO.new).string
|
151
|
+
contents.generate(StringIO.new).string
|
488
152
|
end
|
489
153
|
|
490
154
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
"theme": "oreilly", // テーマ(省略すると basic )
|
4
4
|
// "markdown": "kramdown", // 使用する markdownライブラリ(省略すると redcarpet )
|
5
5
|
// "formats": ["markdown"], // "markdown", "rdoc", "textile" or "wiki" (要 Tilt に対応した gem ライブラリ)
|
6
|
+
// "public": ["styles.css"], // build で出力されるファイル
|
7
|
+
// "requires": [], // require libraries
|
6
8
|
|
7
9
|
// redcarpet の拡張構文
|
8
10
|
// "tables": false, // テーブル
|