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
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, // テーブル
|