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