pbsimply 2.0.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/pbsimply.rb CHANGED
@@ -1,11 +1,29 @@
1
1
  #!/bin/env ruby
2
+
2
3
  require 'yaml'
3
4
  require 'erb'
4
5
  require 'date'
6
+ require 'time'
7
+ require 'tmpdir'
5
8
  require 'fileutils'
6
9
  require 'optparse'
10
+ require 'digest/sha1'
11
+
12
+ # PureBuilder Simply Components
13
+ require 'pbsimply/docdb'
14
+ require 'pbsimply/docengine/base'
15
+ require 'pbsimply/prayer'
16
+ require 'pbsimply/plugger'
17
+ require 'pbsimply/hooks'
18
+ require 'pbsimply/frontmatter'
19
+ require 'pbsimply/accs'
7
20
 
8
21
  class PBSimply
22
+ include Prayer
23
+ include Plugger
24
+ include Frontmatter
25
+ include ACCS
26
+
9
27
  # Use Oj as JSON library for frontmatter passing if possible.
10
28
  begin
11
29
  require 'oj'
@@ -15,141 +33,6 @@ class PBSimply
15
33
  JSON_LIB = JSON
16
34
  end
17
35
 
18
- # ACCS namespace.
19
- module ACCS
20
- DEFINITIONS = {}
21
-
22
- # Built-in Accs index eRuby string.
23
- INDEX = <<'EOF'
24
- <%= YAML.dump(
25
- {
26
- "title" => @index["title"],
27
- "date" => @index["date"],
28
- "author" => @index["author"]
29
- }
30
- ) %>
31
- ---
32
-
33
- <%
34
- articles = Hash.new {|h,k| h[k] = Array.new }
35
-
36
- if @config["accs_across_category"]
37
- @indexes.each {|filename, index| articles["default"].push index }
38
- else
39
- @indexes.each {|filename, index| articles[(index["category"] || "default")].push index }
40
- end
41
-
42
- %>
43
-
44
- % articles.keys.sort.each do |catname|
45
- % cat = articles[catname]
46
-
47
- % unless articles.length == 1
48
- # <%= catname %>
49
- % end
50
-
51
- <%
52
- sort_method = case @config["accs_sort_by"]
53
- when "title"
54
- lambda {|i| [i["title"].to_s, i["date"]] }
55
- when "name"
56
- lambda {|i| [i["_filename"].to_s, i["title"].to_s, i["date"]] }
57
- when "serial"
58
- lambda {|i| [i["serial"].to_s, i["date"], i["_filename"].to_s] }
59
- else
60
- lambda {|i| [i["date"], i["title"].to_s, i["_last_update"].to_i] }
61
- end
62
-
63
- list = if @config["accs_order"] == "desc"
64
- cat.sort_by(&sort_method).reverse
65
- else
66
- cat.sort_by(&sort_method)
67
- end
68
-
69
- list.each do |i|
70
- %>* [<%= i["title"] %>](<%= i["page_url"] %>)
71
- <% end %>
72
-
73
- % end
74
- EOF
75
- end
76
-
77
- # Abstruct super class.
78
- class DocDB
79
- def dump(object)
80
- File.open(File.join(@dir, ".indexes.#{@ext}"), "w") do |f|
81
- f.write @store_class.dump(object)
82
- end
83
- end
84
-
85
- def load
86
- File.open(File.join(@dir, ".indexes.#{@ext}"), "r") do |f|
87
- next @store_class.load(f)
88
- end
89
- end
90
-
91
- def exist?
92
- File.exist?(File.join(@dir, ".indexes.#{@ext}"))
93
- end
94
-
95
- def path
96
- File.join(@dir, ".indexes.#{@ext}")
97
- end
98
-
99
- def cmp_obj(frontmatter)
100
- @store_class.load(@store_class.dump(frontmatter))
101
- end
102
-
103
- class Marshal < DocDB
104
- def initialize(dir)
105
- @dir = dir
106
- @store_class = ::Marshal
107
- @ext = "rbm"
108
- end
109
-
110
- def cmp_obj(frontmatter)
111
- frontmatter.dup
112
- end
113
- end
114
-
115
- class JSON < DocDB
116
- def initialize(dir)
117
- @dir = dir
118
- @store_class = ::JSON
119
- @ext = "json"
120
- end
121
- end
122
-
123
- class Oj < DocDB::JSON
124
- def initialize(dir)
125
- require 'oj'
126
- @dir = dir
127
- @ext = "json"
128
- @store_class = ::Oj
129
- end
130
- end
131
-
132
- class YAML < DocDB
133
- def initialize(dir)
134
- @dir = dir
135
- @store_class = ::YAML
136
- @ext = "yaml"
137
- end
138
- end
139
- end
140
-
141
-
142
- POST_PROCESSORS = {
143
- ".rb" => "ruby",
144
- ".pl" => "perl",
145
- ".py" => "python",
146
- ".lua" => "lua",
147
- ".bash" => "bash",
148
- ".zsh" => "zsh",
149
- ".php" => "php",
150
- ".sed" => ["sed", ->(script, target) { ["-f", script, target] } ]
151
- }
152
-
153
36
  ###############################################
154
37
  # SETUP FUNCTIONS #
155
38
  ###############################################
@@ -183,8 +66,6 @@ EOF
183
66
  outdir = [@config["outdir"], @dir].join("/")
184
67
  end
185
68
 
186
- p dir
187
-
188
69
  # Format for Indexes database
189
70
  @db = case @config["dbstyle"]
190
71
  when "yaml"
@@ -225,6 +106,7 @@ EOF
225
106
  @accs = nil
226
107
  @accs_index = {}
227
108
  @now = Time.now
109
+ @hooks = PBSimply::Hooks.new(self)
228
110
  end
229
111
 
230
112
  # Process command-line
@@ -234,6 +116,7 @@ EOF
234
116
  opts.on("-f", "--force-refresh") { @refresh = true }
235
117
  opts.on("-X", "--ignore-ext") { @ignore_ext = true }
236
118
  opts.on("-I", "--skip-index") { @skip_index = true }
119
+ opts.on("-A", "--skip-accs") { @skip_accs = true }
237
120
  opts.on("-o FILE", "--output") {|v| @outfile = v }
238
121
  opts.on("-m FILE", "--additional-metafile") {|v| @add_meta = Psych.unsafe_load(File.read(v))}
239
122
  opts.parse!(ARGV)
@@ -256,6 +139,7 @@ EOF
256
139
  @indexes = Hash.new
257
140
  end
258
141
  @docobject[:indexes] = @indexes
142
+ ENV["pbsimply_indexes"] = @db.path
259
143
  end
260
144
 
261
145
  def target_file_extensions
@@ -269,7 +153,6 @@ EOF
269
153
 
270
154
  attr :indexes
271
155
 
272
-
273
156
  ###############################################
274
157
  # PROCESSING FUNCTIONS #
275
158
  ###############################################
@@ -284,12 +167,15 @@ EOF
284
167
 
285
168
  STDERR.puts "Checking Frontmatter..."
286
169
  Dir.foreach(@dir) do |filename|
287
- next if filename == "." || filename == ".."
170
+ next if filename == "." || filename == ".." || filename == ".index.md"
288
171
  if filename =~ /^\./ || filename =~ /^draft-/
289
- draft_articles.push filename.sub(/^(?:\.|draft-)/, "")
172
+ draft_articles.push({
173
+ article_filename: filename.sub(/^(?:\.|draft-)/, ""),
174
+ filename: filename,
175
+ source_file_path: File.join(@dir, filename)
176
+ })
290
177
  next
291
178
  end
292
- next unless File.file?([@dir, filename].join("/"))
293
179
 
294
180
  if !@ignore_ext and not target_file_extensions.include? File.extname filename
295
181
  next
@@ -301,8 +187,11 @@ EOF
301
187
  frontmatter.merge!(@add_meta) if @add_meta
302
188
 
303
189
  if frontmatter["draft"]
304
- @indexes.delete(filename) if @indexes[filename]
305
- draft_articles.push filename
190
+ draft_articles.push({
191
+ article_filename: filename,
192
+ filename: filename,
193
+ source_file_path: File.join(@dir, filename)
194
+ })
306
195
  next
307
196
  end
308
197
 
@@ -313,33 +202,35 @@ EOF
313
202
  target_docs.push([filename, frontmatter, pos])
314
203
  end
315
204
 
316
- # Delete turn to draft article.
317
- draft_articles.each do |df|
318
- STDERR.puts "#{df} was turn to draft. deleting..."
319
- [df, (df + ".html"), File.basename(df, ".*"), (File.basename(df, ".*") + ".html")].each do |tfn|
320
- tfp = File.join(@config["outdir"], @dir, tfn)
321
- File.delete tfp if File.file?(tfp)
322
- end
323
- end
205
+ delete_turn_draft draft_articles
206
+
207
+ proc_docs target_docs
208
+
209
+ delete_missing
324
210
 
325
211
  # Save index.
326
212
  @db.dump(@indexes) unless @skip_index
327
213
 
328
- STDERR.puts "Blessing..."
329
-
330
- # Modify frontmatter `BLESSING`
331
- target_docs.each do |filename, frontmatter, pos|
332
- if @config["bless_style"] == "cmd"
333
- bless_cmd(frontmatter)
334
- else
335
- bless_ruby(frontmatter)
336
- end
214
+ # ACCS processing
215
+ if @accs && !target_docs.empty?
216
+ process_accs
337
217
  end
218
+ end
338
219
 
339
- STDERR.puts "Checking modification..."
220
+ def proc_docs target_docs
221
+ # Exclude unchanged documents.
222
+ if @indexes && @indexes_orig
223
+ STDERR.puts "Checking modification..."
224
+ target_docs.delete_if {|filename, frontmatter, pos| !check_modify([@dir, filename], frontmatter)}
225
+ end
340
226
 
341
- target_docs.delete_if {|filename, frontmatter, pos| !check_modify([@dir, filename], frontmatter)}
227
+ # Modify frontmatter `BLESSING'
228
+ target_docs.each do |filename, frontmatter, pos|
229
+ STDERR.puts "Blessing #{filename}..."
230
+ bless frontmatter
231
+ end
342
232
 
233
+ # Ready.
343
234
  STDERR.puts "Okay, Now ready. Process documents..."
344
235
 
345
236
  # Proccess documents
@@ -348,135 +239,107 @@ EOF
348
239
  @index = frontmatter
349
240
  File.open(File.join(@dir, filename)) do |f|
350
241
  f.seek(pos)
351
- File.open(".current_document#{ext}", "w") {|fo| fo.write f.read}
242
+ File.open(File.join(@workdir, "current_document#{ext}"), "w") {|fo| fo.write f.read}
352
243
  end
353
244
 
354
245
  STDERR.puts "Processing #{filename}"
355
246
  generate(@dir, filename, frontmatter)
356
247
  end
357
-
358
- @db.dump(@indexes) unless @skip_index
359
-
248
+
249
+ # Call post plugins
360
250
  post_plugins
361
251
 
362
- # ACCS processing
363
- if @accs && !target_docs.empty?
364
- process_accs
365
- end
252
+ # Call hooks
253
+ @hooks.post.run({this_time_processed: @this_time_processed})
366
254
  end
367
255
 
368
- # Run PureBuilder Simply.
369
- def main
370
- # If target file is regular file, run as single mode.
371
- @singlemode = true if File.file?(@dir)
372
-
373
- # Check single file mode.
374
- if @singlemode
375
- # Single file mode
376
- if @dir =~ %r:(.*)/([^/]+):
377
- dir = $1
378
- filename = $2
379
- else
380
- dir = "."
381
- filename = @dir
256
+ # Delete turn to draft article.
257
+ def delete_turn_draft draft_articles
258
+ STDERR.puts "Checking turn to draft..."
259
+ draft_articles.each do |dah|
260
+ df = dah[:article_filename]
261
+ [df, (df + ".html"), File.basename(df, ".*"), (File.basename(df, ".*") + ".html")].each do |tfn|
262
+ tfp = File.join(@config["outdir"], @dir, tfn)
263
+ if File.file?(tfp)
264
+ STDERR.puts "#{df} was turn to draft."
265
+ @hooks.delete.run({target_file_path: tfp, source_file_path: dah[:source_file_path]})
266
+ File.delete tfp if @config["auto_delete"]
267
+ end
382
268
  end
383
- @dir = dir
384
- setup_config(dir)
269
+ @indexes.delete df if @indexes[df]
270
+ end
271
+ end
385
272
 
386
- load_index
273
+ # Delete missing source
274
+ def delete_missing
275
+ return unless @indexes
276
+ STDERR.puts "Checking missing article..."
277
+ missing_keys = []
278
+ @indexes.each do |k, v|
279
+ next if !v["source_path"] || !v["dest_path"]
280
+ unless File.exist? v["source_path"]
281
+ STDERR.puts "#{k} is missing."
282
+ missing_keys.push k
283
+ @hooks.delete.run({target_file_path: v["dest_path"] ,source_file_path: v["source_path"]})
284
+ File.delete v["dest_path"] if @config["auto_delete"]
285
+ end
286
+ end
287
+ missing_keys.each {|k| @indexes.delete k }
288
+ end
387
289
 
388
- frontmatter, pos = read_frontmatter(dir, filename)
389
- frontmatter = @frontmatter.merge frontmatter
390
- @index = frontmatter
290
+ # Run PureBuilder Simply.
291
+ def main
292
+ @hooks.load
293
+ Dir.mktmpdir("pbsimply") do |dir|
294
+ ENV["pbsimply_working_dir"] = dir
295
+ @workdir ||= dir
296
+ @workfile_frontmatter ||= File.join(@workdir, "pbsimply-frontmatter.json")
297
+ ENV["pbsimply_frontmatter"] = @workfile_frontmatter
298
+ @workfile_pandoc_defaultfiles ||= File.join(@workdir, "pbsimply-defaultfiles.yaml")
299
+ # If target file is regular file, run as single mode.
300
+ @singlemode = true if File.file?(@dir)
301
+
302
+ # Check single file mode.
303
+ if @singlemode
304
+ # Single file mode
305
+ if @dir =~ %r:(.*)/([^/]+):
306
+ dir = $1
307
+ filename = $2
308
+ else
309
+ dir = "."
310
+ filename = @dir
311
+ end
312
+ @dir = dir
313
+ setup_config(dir)
391
314
 
392
- ext = File.extname filename
393
- File.open(File.join(dir, filename)) do |f|
394
- f.seek(pos)
395
- File.open(".current_document#{ext}", "w") {|fo| fo.write f.read}
396
- end
315
+ load_index
397
316
 
398
- generate(dir, filename, frontmatter)
317
+ frontmatter, pos = read_frontmatter(dir, filename)
318
+ frontmatter = @frontmatter.merge frontmatter
319
+ @index = frontmatter
399
320
 
400
- post_plugins(frontmatter)
321
+ proc_docs([[filename, frontmatter, pos]])
401
322
 
402
- else
403
- # Normal (directory) mode.
404
- setup_config(@dir)
405
- load_index
323
+ if File.exist?(File.join(@dir, ".accs.yaml")) && !@accs_processing && !@skip_accs
324
+ single_accs filename, frontmatter
325
+ end
326
+ else
327
+ # Normal (directory) mode.
328
+ setup_config(@dir)
329
+ load_index
406
330
 
407
- @accs = true if File.exist?(File.join(@dir, ".accs.yaml"))
331
+ @accs = true if File.exist?(File.join(@dir, ".accs.yaml"))
408
332
 
409
- # Check existing in indexes.
410
- @indexes.delete_if {|k,v| ! File.exist?([@dir, k].join("/")) }
333
+ # Check existing in indexes.
334
+ @indexes.delete_if {|k,v| ! File.exist?([@dir, k].join("/")) }
411
335
 
412
- proc_dir
413
- end
414
- ensure
415
- # Clean up temporary files.
416
- Dir.children(".").each do |fn|
417
- if fn[0, 22] == ".pbsimply-defaultfiles.yaml" or
418
- fn[0, 21] == ".pbsimply-frontmatter" or
419
- fn[0, 17] == ".current_document"
420
- File.delete fn
421
- end
422
- end
423
- end
424
-
425
- def pre_plugins(procdoc, frontmatter)
426
- if File.directory?(".pre_generate")
427
- STDERR.puts("Processing with pre plugins")
428
- script_file = File.join(".pre_generate", script_file)
429
- Dir.entries(".pre_generate").sort.each do |script_file|
430
- next if script_file =~ /^\./
431
- STDERR.puts "Running script: #{File.basename script_file}"
432
- pre_script_result = nil
433
- script_cmdline = case
434
- when File.executable?(script_file)
435
- [script_file, procdoc]
436
- when POST_PROCESSORS[File.extname(script_file)]
437
- [POST_PROCESSORS[File.extname(script_file)], script_file, procdoc]
438
- else
439
- ["perl", script_file, procdoc]
440
- end
441
- IO.popen({"pbsimply_doc_frontmatter" => YAML.dump(frontmatter)}, script_cmdline) do |io|
442
- pre_script_result = io.read
443
- end
444
- File.open(procdoc, "w") {|f| f.write pre_script_result}
445
- end
446
- end
447
- end
448
-
449
- def post_plugins(frontmatter=nil)
450
- if File.directory?(".post_generate")
451
-
452
- STDERR.puts("Processing with post plugins")
453
-
454
- @this_time_processed.each do |v|
455
- STDERR.puts "Processing #{v[:dest]} (from #{v[:source]})"
456
- procdoc = v[:dest]
457
- frontmatter ||= @indexes[File.basename v[:source]]
458
- File.open(".pbsimply-frontmatter.json", "w") {|f| f.write JSON_LIB.dump(frontmatter)}
459
- Dir.entries(".post_generate").sort.each do |script_file|
460
- next if script_file =~ /^\./
461
- STDERR.puts "Running script: #{script_file}"
462
- script_file = File.join(".post_generate", script_file)
463
- post_script_result = nil
464
- script_cmdline = case
465
- when File.executable?(script_file)
466
- [script_file, procdoc]
467
- when POST_PROCESSORS[File.extname(script_file)]
468
- [POST_PROCESSORS[File.extname(script_file)], script_file, procdoc]
469
- else
470
- ["perl", script_file, procdoc]
471
- end
472
- IO.popen({"pbsimply_frontmatter" => ".pbsimply-frontmatter.json", "pbsimply_indexes" => @db.path}, script_cmdline) do |io|
473
- post_script_result = io.read
474
- end
475
-
476
- File.open(procdoc, "w") {|f| f.write post_script_result}
477
- end
336
+ proc_dir
478
337
  end
479
338
  end
339
+ ensure
340
+ @workdir = nil
341
+ @workfile_frontmatter = nil
342
+ @workfile_pandoc_defaultfiles = nil
480
343
  end
481
344
 
482
345
  def generate(dir, filename, frontmatter)
@@ -485,8 +348,10 @@ EOF
485
348
  # Preparing and pre script.
486
349
  orig_filepath = [dir, filename].join("/")
487
350
  ext = File.extname(filename)
488
- procdoc = sprintf(".current_document%s", ext)
351
+ procdoc = File.join(@workdir, "current_document#{ext}")
352
+
489
353
  pre_plugins(procdoc, frontmatter)
354
+ @hooks.pre.run({procdoc: procdoc, frontmatter: frontmatter})
490
355
 
491
356
  doc = process_document(dir, filename, frontmatter, orig_filepath, ext, procdoc) # at sub-class
492
357
 
@@ -497,54 +362,21 @@ EOF
497
362
  end
498
363
 
499
364
  # Write out
500
- outext = frontmatter["force_ext"] || ".html"
501
- outpath = case
502
- when @outfile
503
- @outfile
504
- when @accs_processing
505
- File.join(@config["outdir"], @dir, "index") + outext
506
- else
507
- File.join(@config["outdir"], @dir, File.basename(filename, ".*")) + outext
508
- end
365
+ outpath = frontmatter["dest_path"]
509
366
 
510
367
  File.open(outpath, "w") do |f|
511
368
  f.write(doc)
512
369
  end
513
370
 
514
- # Mark processed
515
- @this_time_processed.push({source: orig_filepath, dest: outpath})
516
- end
517
-
518
- # letsaccs
519
- #
520
- # This method called on the assumption that processed all documents and run as directory mode.
521
- def process_accs
522
- STDERR.puts "Processing ACCS index..."
523
- if File.exist?(File.join(@dir, ".accsindex.erb"))
524
- erbtemplate = File.read(File.join(@dir, ".accsindex.erb"))
525
- elsif File.exist?(".accsindex.erb")
526
- erbtemplate = File.read(".accsindex.erb")
527
- else
528
- erbtemplate = ACCS::INDEX
529
- end
530
-
531
- # Get infomation
532
- @accs_index = Psych.unsafe_load(File.read([@dir, ".accs.yaml"].join("/")))
533
-
534
- @accs_index["title"] ||= (@config["accs_index_title"] || "Index")
535
- @accs_index["date"] ||= Time.now.strftime("%Y-%m-%d")
536
- @accs_index["pagetype"] = "accs_index"
537
-
538
- @index = @frontmatter.merge @accs_index
539
-
540
- doc = ERB.new(erbtemplate, trim_mode: "%<>").result(binding)
541
- File.open(File.join(@dir, ".index.md"), "w") do |f|
542
- f.write doc
543
- end
371
+ # Hooks for processed document.
372
+ @hooks.process.run({
373
+ outpath: outpath,
374
+ frontmatter: frontmatter,
375
+ procdoc: procdoc
376
+ })
544
377
 
545
- accsmode
546
- @dir = File.join(@dir, ".index.md")
547
- main
378
+ # Mark processed
379
+ @this_time_processed.push({source: orig_filepath, dest: outpath, frontmatter: frontmatter})
548
380
  end
549
381
 
550
382
  ###############################################
@@ -553,198 +385,21 @@ EOF
553
385
 
554
386
  private
555
387
 
556
- # Turn on ACCS processing mode.
557
- def accsmode
558
- @accs_processing = true
559
- @singlemode = true
560
- @skip_index = true
561
- end
562
-
563
- # Read Frontmatter from the document.
564
- # This method returns frontmatter, pos.
565
- # pos means position at end of Frontmatter on the file.
566
- def read_frontmatter(dir, filename)
567
- frontmatter = nil
568
- pos = nil
569
-
570
- if File.exist? File.join(dir, ".meta." + filename)
571
- # Load standalone metadata YAML.
572
- frontmatter = Psych.unsafe_load(File.read(File.join(dir, (".meta." + filename))))
573
- pos = 0
574
- else
575
-
576
- case File.extname filename
577
- when ".md"
578
-
579
- # Load Markdown's YAML frontmatter.
580
- File.open(File.join(dir, filename)) do |f|
581
- l = f.gets
582
- next unless l && l.chomp == "---"
583
-
584
- lines = []
585
-
586
- while l = f.gets
587
- break if l.nil?
588
-
589
- break if l.chomp == "---"
590
- lines.push l
591
- end
592
-
593
- next if f.eof?
594
-
595
- begin
596
- frontmatter = Psych.unsafe_load(lines.join)
597
- rescue => e
598
- STDERR.puts "!CRITICAL: Cannot parse frontmatter."
599
- raise e
600
- end
601
-
602
- pos = f.pos
603
- end
604
-
605
- when ".rst"
606
- # ReSTRUCTURED Text
607
-
608
- File.open(File.join(dir, filename)) do |f|
609
- l = f.gets
610
- if l =~ /:([A-Za-z_-]+): (.*)/ #docinfo
611
- frontmatter = { $1 => [$2.chomp] }
612
- last_key = $1
613
-
614
- # Read docinfo
615
- while(l = f.gets)
616
- break if l =~ /^\s*$/ # End of docinfo
617
- if l =~ /^\s+/ # Continuous line
618
- docinfo_lines.last.push($'.chomp)
619
- elsif l =~ /:([A-Za-z_-]+): (.*)/
620
- frontmatter[$1] = [$2.chomp]
621
- last_key = $1
622
- end
623
- end
624
-
625
- # Treat docinfo lines
626
- frontmatter.each do |k,v|
627
- v = v.join(" ")
628
- #if((k == "author" || k == "authors") && v.include?(";")) # Multiple authors.
629
- if(v.include?(";")) # Multiple element.
630
- v = v.split(/\s*;\s*/)
631
-
632
- elsif k == "date" # Date?
633
- # Datetime?
634
- if v =~ /[0-2][0-9]:[0-6][0-9]/
635
- v = DateTime.parse(v)
636
- else
637
- v = Date.parse(v)
638
- end
639
- elsif v == "yes" || v == "true"
640
- v = true
641
- else # Simple String.
642
- nil # keep v
643
- end
644
-
645
- frontmatter[k] = v
646
- end
647
-
648
- elsif l && l.chomp == ".." #YAML
649
- # Load ReST YAML that document begins comment and block is yaml.
650
- lines = []
651
-
652
- while(l = f.gets)
653
- if(l !~ /^\s*$/ .. l =~ /^\s*$/)
654
- if l=~ /^\s*$/
655
- break
656
- else
657
- lines.push l
658
- end
659
- end
660
- end
661
- next if f.eof?
662
-
663
-
664
- # Rescue for failed to read YAML.
665
- begin
666
- frontmatter = Psych.unsafe_load(lines.map {|i| i.sub(/^\s*/, "") }.join)
667
- rescue
668
- STDERR.puts "Error in parsing ReST YAML frontmatter (#{$!})"
669
- next
670
- end
671
- else
672
- next
673
- end
674
-
675
- pos = f.pos
676
-
677
- end
678
- end
679
- end
680
-
681
- abort "This document has no frontmatter" unless frontmatter
682
- abort "This document has no title." unless frontmatter["title"]
683
-
684
-
685
- ### Additional meta values. ###
686
- frontmatter["source_directory"] = dir # Source Directory
687
- frontmatter["source_filename"] = filename # Source Filename
688
- frontmatter["source_path"] = File.join(dir, filename) # Source Path
689
- # URL in site.
690
- this_url = (File.join(dir, filename)).sub(/^[\.\/]*/) { @config["self_url_prefix"] || "/" }.sub(/\.[a-zA-Z0-9]+$/, ".html")
691
- frontmatter["page_url"] = this_url
692
- # URL in site with URI encode.
693
- frontmatter["page_url_encoded"] = ERB::Util.url_encode(this_url)
694
- frontmatter["page_url_encoded_external"] = ERB::Util.url_encode((File.join(dir, filename)).sub(/^[\.\/]*/) { @config["self_url_external_prefix"] || "/" }.sub(/\.[a-zA-Z0-9]+$/, ".html"))
695
- frontmatter["page_html_escaped"] = ERB::Util.html_escape(this_url)
696
- frontmatter["page_html_escaped_external"] = ERB::Util.html_escape((File.join(dir, filename)).sub(/^[\.\/]*/) { @config["self_url_external_prefix"] || "/" }.sub(/\.[a-zA-Z0-9]+$/, ".html"))
697
- # Title with URL Encoded.
698
- frontmatter["title_encoded"] = ERB::Util.url_encode(frontmatter["title"])
699
- frontmatter["title_html_escaped"] = ERB::Util.html_escape(frontmatter["title"])
700
- fts = frontmatter["timestamp"]
701
- fts = fts.to_datetime if Time === fts
702
- if DateTime === fts
703
- frontmatter["timestamp_xmlschema"] = fts.xmlschema
704
- frontmatter["timestamp_jplocal"] = fts.strftime('%Y年%m月%d日 %H時%M分%S秒')
705
- frontmatter["timestamp_rubytimestr"] = fts.strftime('%a %b %d %H:%M:%S %Z %Y')
706
- frontmatter["timestamp_str"] = fts.strftime("%Y-%m-%d %H:%M:%S %Z")
707
- elsif Date === fts
708
- frontmatter["timestamp_xmlschema"] = fts.xmlschema
709
- frontmatter["timestamp_jplocal"] = fts.strftime('%Y年%m月%d日')
710
- frontmatter["timestamp_rubytimestr"] = fts.strftime('%a %b %d')
711
- frontmatter["timestamp_str"] = fts.strftime("%Y-%m-%d")
712
- elsif Date === frontmatter["Date"]
713
- fts = frontmatter["Date"]
714
- frontmatter["timestamp_xmlschema"] = fts.xmlschema
715
- frontmatter["timestamp_jplocal"] = fts.strftime('%Y年%m月%d日')
716
- frontmatter["timestamp_rubytimestr"] = fts.strftime('%a %b %d')
717
- frontmatter["timestamp_str"] = fts.strftime("%Y-%m-%d")
718
- end
719
-
720
- fsize = FileTest.size(File.join(dir, filename))
721
- mtime = File.mtime(File.join(dir, filename)).to_i
722
-
723
- frontmatter["_filename"] ||= filename
724
- frontmatter["pagetype"] ||= "post"
725
-
726
- frontmatter["_size"] = fsize
727
- frontmatter["_mtime"] = mtime
728
- frontmatter["_last_proced"] = @now.to_i
729
-
730
- if File.extname(filename) == ".md"
731
- frontmatter["_docformat"] = "Markdown"
732
- elsif File.extname(filename) == ".rst" || File.extname(filename) == ".rest"
733
- frontmatter["_docformat"] = "ReST"
734
- end
735
-
736
- frontmatter["date"] ||= @now.strftime("%Y-%m-%d %H:%M:%S")
737
-
738
- return frontmatter, pos
739
- end
740
-
741
388
  # Check is the article modified? (or force update?)
742
389
  def check_modify(path, frontmatter)
743
390
  modify = true
744
391
  index = @indexes_orig[path[1]].dup || {}
745
392
  frontmatter = @db.cmp_obj(frontmatter)
393
+
394
+ # Remove unstable metadata for compartion.
746
395
  index.delete("_last_proced")
396
+ index.delete("source_path")
397
+ index.delete("source_directory")
398
+ index.delete("dest_path")
747
399
  frontmatter.delete("_last_proced")
400
+ frontmatter.delete("source_path")
401
+ frontmatter.delete("source_directory")
402
+ frontmatter.delete("dest_path")
748
403
 
749
404
  if index == frontmatter
750
405
  STDERR.puts "#{path[1]} is not modified."
@@ -761,311 +416,4 @@ EOF
761
416
  modify
762
417
  end
763
418
  end
764
-
765
- def bless_ruby(frontmatter)
766
- # BLESSING (Always)
767
- if PureBuilder.const_defined?(:BLESS) && Proc === PureBuilder::BLESS
768
- begin
769
- PureBuilder::BLESS.(frontmatter, self)
770
- rescue
771
- STDERR.puts "*** BLESSING PROC ERROR ***"
772
- raise
773
- end
774
- end
775
-
776
- # BLESSING (ACCS)
777
- if @accs && PureBuilder::ACCS.const_defined?(:BLESS) && Proc === PureBuilder::ACCS::BLESS
778
- begin
779
- PureBuilder::ACCS::BLESS.(frontmatter, self)
780
- rescue
781
- STDERR.puts "*** ACCS BLESSING PROC ERROR ***"
782
- raise
783
- end
784
- end
785
-
786
- # ACCS DEFINITIONS
787
- if @accs
788
- if Proc === PureBuilder::ACCS::DEFINITIONS[:next]
789
- i = PureBuilder::ACCS::DEFINITIONS[:next].call(frontmatter, self)
790
- frontmatter["next_article"] = i if i
791
- end
792
- if Proc === PureBuilder::ACCS::DEFINITIONS[:prev]
793
- i = PureBuilder::ACCS::DEFINITIONS[:prev].call(frontmatter, self)
794
- frontmatter["prev_article"] = i if i
795
- end
796
- end
797
-
798
- autobless(frontmatter)
799
- end
800
-
801
- def bless_cmd(frontmatter)
802
- File.open(".pbsimply-frontmatter.json", "w") {|f| f.write JSON_LIB.dump(frontmatter) }
803
- # BLESSING (Always)
804
- if @config["bless_cmd"]
805
- (Array === @config["bless_cmd"] ? system(*@config["bless_cmd"]) : system(@config["bless_cmd"]) ) or abort "*** BLESS COMMAND RETURNS NON-ZERO STATUS"
806
- end
807
- # BLESSING (ACCS)
808
- if @config["bless_accscmd"]
809
- (Array === @config["bless_accscmd"] ? system({"pbsimply_frontmatter" => ".pbsimply-frontmatter.json", "pbsimply_indexes" => @db.path}, *@config["bless_accscmd"]) : system({"pbsimply_frontmatter" => ".pbsimply-frontmatter.json", "pbsimply_indexes" => @db.path}, @config["bless_accscmd"]) ) or abort "*** BLESS COMMAND RETURNS NON-ZERO STATUS"
810
- end
811
- mod_frontmatter = JSON.load(File.read(".pbsimply-frontmatter.json"))
812
- frontmatter.replace(mod_frontmatter)
813
-
814
- autobless(frontmatter)
815
- end
816
-
817
- # Blessing automatic method with configuration.
818
- def autobless(frontmatter)
819
- catch(:accs_rel) do
820
- # find Next/Prev page on accs
821
- if @accs && @config["blessmethod_accs_rel"]
822
- # Preparing. Run at once.
823
- if !@article_order
824
- @rev_article_order_index = {}
825
-
826
- case @config["blessmethod_accs_rel"]
827
- when "numbering"
828
- @article_order = @indexes.to_a.sort_by {|i| i[1]["_filename"].to_i }
829
- when "date"
830
- begin
831
- @article_order = @indexes.to_a.sort_by {|i| i[1]["date"]}
832
- rescue
833
- abort "*** Automatic Blessing Method Error: Maybe some article have no date."
834
- end
835
- when "timestamp"
836
- begin
837
- @article_order = @indexes.to_a.sort_by {|i| i[1]["timestamp"]}
838
- rescue
839
- abort "*** Automatic Blessing Method Error: Maybe some article have no timetsamp."
840
- end
841
- when "lexical"
842
- @article_order = @indexes.to_a.sort_by {|i| i[1]["_filename"]}
843
- end
844
- @article_order.each_with_index {|x,i| @rev_article_order_index[x[0]] = i }
845
- end
846
-
847
- throw(:accs_rel) unless index = @rev_article_order_index[frontmatter["_filename"]]
848
- if @article_order[index + 1]
849
- frontmatter["next_article"] = {"url" => @article_order[index + 1][1]["page_url"],
850
- "title" => @article_order[index + 1][1]["title"]}
851
- end
852
- if index > 0
853
- frontmatter["prev_article"] = {"url" => @article_order[index - 1][1]["page_url"],
854
- "title" => @article_order[index - 1][1]["title"]}
855
- end
856
- end
857
- end
858
- end
859
-
860
- ###############################################
861
- # DOCUMENT PROCESSORS #
862
- ###############################################
863
-
864
-
865
- module Processor
866
-
867
- # Pandoc processor
868
- class Pandoc < PBSimply
869
- def initialize(config)
870
- @pandoc_default_file = {}
871
-
872
- # -d
873
- @pandoc_default_file = {
874
- "to" => "html5",
875
- "standalone" => true
876
- }
877
- super
878
- end
879
-
880
- def setup_config(dir)
881
- super
882
- @pandoc_default_file["template"] = @config["template"]
883
-
884
- if @config["css"]
885
- if @config["css"].kind_of?(String)
886
- @pandoc_default_file["css"] = [@config["css"]]
887
- elsif @config["css"].kind_of?(Array)
888
- @pandoc_default_file["css"] = @config["css"]
889
- else
890
- abort "css in Config should be a String or an Array."
891
- end
892
- end
893
-
894
- if @config["toc"]
895
- @pandoc_default_file["toc"] = true
896
- end
897
-
898
- if Hash === @config["pandoc_additional_options"]
899
- @pandoc_default_file.merge! @config["pandoc_additional_options"]
900
- end
901
-
902
- end
903
-
904
- # Invoke pandoc, parse and format and write out.
905
- def print_fileproc_msg(filename)
906
- STDERR.puts "#{filename} is going Pandoc."
907
- end
908
-
909
- def process_document(dir, filename, frontmatter, orig_filepath, ext, procdoc)
910
- doc = nil
911
-
912
- File.open(".pbsimply-defaultfiles.yaml", "w") {|f| YAML.dump(@pandoc_default_file, f)}
913
- File.open(".pbsimply-frontmatter.yaml", "w") {|f| YAML.dump(frontmatter, f)}
914
-
915
- # Go Pandoc
916
- pandoc_cmdline = ["pandoc"]
917
- pandoc_cmdline += ["-d", ".pbsimply-defaultfiles.yaml", "--metadata-file", ".pbsimply-frontmatter.yaml", "-M", "title:#{frontmatter["title"]}"]
918
- pandoc_cmdline += ["-f", frontmatter["input_format"]] if frontmatter["input_format"]
919
- pandoc_cmdline += [ procdoc ]
920
- IO.popen((pandoc_cmdline)) do |io|
921
- doc = io.read
922
- end
923
-
924
- # Abort if pandoc returns non-zero status
925
- if $?.exitstatus != 0
926
- abort "Pandoc returns exit code #{$?.exitstatus}"
927
- end
928
-
929
- doc
930
- end
931
-
932
- def target_file_extensions
933
- [".md", ".rst"]
934
- end
935
- end
936
-
937
- # RDoc family Base
938
- class PbsRBase < PBSimply
939
- def initialize(config)
940
- require 'rdoc'
941
- require 'rdoc/markup/to_html'
942
-
943
- @rdoc_options = RDoc::Options.new
944
- @rdoc_markup = RDoc::Markup.new
945
-
946
- super
947
- end
948
-
949
- def process_document(dir, filename, frontmatter, orig_filepath, ext, procdoc)
950
- # Getting HTML string.
951
- rdoc = RDoc::Markup::ToHtml.new(@rdoc_options, @rdoc_markup)
952
- article_body = rdoc.convert(get_markup_document(procdoc))
953
-
954
- # Process with eRuby temaplte.
955
- erb_template = ERB.new(File.read(@config["template"]), trim_mode: '%<>')
956
- doc = erb_template.result(binding)
957
-
958
- doc
959
- end
960
- end
961
-
962
- # RDoc/Markdown processor
963
- class PbsRMakrdown < PbsRBase
964
- def initialize(config)
965
- require 'rdoc'
966
- require 'rdoc/markdown'
967
- super
968
- end
969
-
970
- def print_fileproc_msg(filename)
971
- STDERR.puts "#{filename} generate with RDoc/Markdown"
972
- end
973
-
974
- def get_markup_document procdoc
975
- RDoc::Markdown.parse(File.read procdoc)
976
- end
977
- end
978
-
979
- # RDoc processor
980
- class PbsRDoc < PbsRBase
981
- def print_fileproc_msg(filename)
982
- STDERR.puts "#{filename} generate with RDoc"
983
- end
984
-
985
- def get_markup_document procdoc
986
- File.read procdoc
987
- end
988
-
989
- def target_file_extensions
990
- [".rdoc"]
991
- end
992
- end
993
-
994
- class PbsRedCarpet < PBSimply
995
- def initialize(config)
996
- require 'redcarpet'
997
- super
998
- end
999
-
1000
- def setup_config(dir)
1001
- super
1002
- @rc_extension = @config["redcarpet_extensions"] || {}
1003
- end
1004
-
1005
- def print_fileproc_msg(filename)
1006
- STDERR.puts "#{filename} generate with Redcarpet Markdown"
1007
- end
1008
-
1009
- def process_document(dir, filename, frontmatter, orig_filepath, ext, procdoc)
1010
- # Getting HTML string.
1011
- markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, **@rc_extension)
1012
- article_body = markdown.render(File.read procdoc)
1013
-
1014
- # Process with eRuby temaplte.
1015
- erb_template = ERB.new(File.read(@config["template"]), trim_mode: '%<>')
1016
- doc = erb_template.result(binding)
1017
-
1018
- doc
1019
- end
1020
- end
1021
-
1022
- class PbsKramdown < PBSimply
1023
- def initialize(config)
1024
- require 'kramdown'
1025
- super
1026
- end
1027
-
1028
- def print_fileproc_msg(filename)
1029
- STDERR.puts "#{filename} generate with Kramdown"
1030
- end
1031
-
1032
- def process_document(dir, filename, frontmatter, orig_filepath, ext, procdoc)
1033
- # Set feature options
1034
- features = @config["kramdown_features"] || {}
1035
-
1036
- # Getting HTML string.
1037
- markdown = Kramdown::Document.new(File.read(procdoc), **features)
1038
- article_body = markdown.to_html
1039
-
1040
- # Process with eRuby temaplte.
1041
- erb_template = ERB.new(File.read(@config["template"]), trim_mode: '%<>')
1042
- doc = erb_template.result(binding)
1043
-
1044
- doc
1045
- end
1046
- end
1047
-
1048
- class PbsCommonMark < PBSimply
1049
- def initialize(config)
1050
- require 'commonmarker'
1051
- super
1052
- end
1053
-
1054
- def print_fileproc_msg(filename)
1055
- STDERR.puts "#{filename} generate with CommonMarker (cmark-gfm)"
1056
- end
1057
-
1058
- def process_document(dir, filename, frontmatter, orig_filepath, ext, procdoc)
1059
- # Getting HTML string.
1060
- article_body = CommonMarker.render_doc(File.read(procdoc), :DEFAULT, [:table, :strikethrough]).to_html
1061
-
1062
- # Process with eRuby temaplte.
1063
- erb_template = ERB.new(File.read(@config["template"]), trim_mode: '%<>')
1064
- doc = erb_template.result(binding)
1065
-
1066
- doc
1067
- end
1068
- end
1069
-
1070
- end
1071
419
  end