memorack 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"
@@ -5,3 +5,7 @@ ja:
5
5
  dir: "テーマのディレクトリー(省略値: %{dir})"
6
6
  port: "ポートを使う (省略値: %{Port})"
7
7
  theme: "テーマを使う (省略値: %{theme})"
8
+ output: "出力するディレクトリ (省略値: %{output})"
9
+ url: "サイトURL (省略値: %{url})"
10
+ local: "サイトURLをアウトプットディレクトリにする"
11
+ prettify: "綺麗なURLになるように生成する"
@@ -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('.')
@@ -5,59 +5,15 @@ require 'rubygems'
5
5
  require 'rack'
6
6
  require 'uri'
7
7
 
8
- require 'memorack/tilt-mustache'
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
- options = DEFAULT_OPTIONS.merge(to_sym_keys(options))
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(env, path_info)
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
- content = render_content(env, path_info)
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
- mdmenu = MdMenu.new({prefix: '/', suffix: @suffix, uri_escape: true, formats: collect_formats})
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, // テーブル