pbsimply 2.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d364901d22b774c9f9aecabde031611aec62d3d3edc49e05b47a34abb83f68c8
4
+ data.tar.gz: 779d8544a9e359d4ec94562c92dd898b1995b4e66d55796d3e4659b161e4ec7b
5
+ SHA512:
6
+ metadata.gz: 801b0b10d12598699b8cee2c91bff275f7feed4da3433f9499b633742b65d5a589bd6eee80280403f2c08b8e209014eb3b20a9ebf1905c7dfcdb58c452c044f0
7
+ data.tar.gz: 5f181d1a737bebcc3d28981d8a37ca43e925aab5b08678db20e48272024ab25aaded146e06ac1e5e6026c96dd4bc629a94d67644853dc8f44d0cc77342d01b50
data/bin/pbsimply ADDED
@@ -0,0 +1,26 @@
1
+
2
+ require 'pbsimply'
3
+
4
+ config = PBSimply.load_config
5
+
6
+ pbs_class = case config["pbsimply_processor"]
7
+ when "redcarpet"
8
+ PBSimply::Processor::PbsRedCarpet
9
+ when "kramdown"
10
+ PBSimply::Processor::PbsKramdown
11
+ when "cmark"
12
+ PBSimply::Processor::PbsCommonMark
13
+ when "rdoc_markdown"
14
+ PBSimply::Processor::PbsRMakrdown
15
+ when "rdoc"
16
+ PBSimply::Processor::PbsRDoc
17
+ else
18
+ PBSimply::Processor::Pandoc
19
+ end
20
+
21
+ # Alias for compatibility.
22
+ PureBuilder = pbs_class
23
+
24
+ pbs = pbs_class.new(config)
25
+ pbs.treat_cmdline
26
+ pbs.main
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/ruby
2
+ # -*- mode: ruby; coding: UTF-8 -*-
3
+
4
+ require 'webrick'
5
+ require 'yaml'
6
+
7
+ File.open(".pbsimply.yaml") do |f|
8
+ @config = YAML.load(f)
9
+ end
10
+
11
+ srv = WEBrick::HTTPServer.new({ :DocumentRoot => @config["outdir"],
12
+ :BindAddress => '127.0.0.1',
13
+ :Port => (@config["testserver_port"] || 8000 )})
14
+ trap("INT"){ srv.shutdown }
15
+ srv.start
data/lib/pbsimply.rb ADDED
@@ -0,0 +1,1071 @@
1
+ #!/bin/env ruby
2
+ require 'yaml'
3
+ require 'erb'
4
+ require 'date'
5
+ require 'fileutils'
6
+ require 'optparse'
7
+
8
+ class PBSimply
9
+ # Use Oj as JSON library for frontmatter passing if possible.
10
+ begin
11
+ require 'oj'
12
+ JSON_LIB = Oj
13
+ rescue LoadError
14
+ require 'json'
15
+ JSON_LIB = JSON
16
+ end
17
+
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
+ ###############################################
154
+ # SETUP FUNCTIONS #
155
+ ###############################################
156
+
157
+ # Load config file.
158
+ def self.load_config
159
+ config = nil
160
+ begin
161
+ File.open(".pbsimply.yaml") do |f|
162
+ config = YAML.load(f)
163
+ end
164
+ rescue
165
+ abort "Failed to load config file (./.pbsimply.yaml)"
166
+ end
167
+
168
+ # Required values
169
+ config["outdir"] or abort "Output directory is not set (outdir)."
170
+ config["template"] ||= "./template.html"
171
+
172
+ config
173
+ end
174
+
175
+ # initialize phase,
176
+ def setup_config(dir)
177
+ ENV["pbsimply_outdir"] = @config["outdir"]
178
+ @docobject[:config] = @config
179
+
180
+ if @singlemode
181
+ outdir = [@config["outdir"], @dir.sub(%r:/[^/]*$:, "")].join("/")
182
+ else
183
+ outdir = [@config["outdir"], @dir].join("/")
184
+ end
185
+
186
+ p dir
187
+
188
+ # Format for Indexes database
189
+ @db = case @config["dbstyle"]
190
+ when "yaml"
191
+ DocDB::YAML.new(dir)
192
+ when "json"
193
+ DocDB::JSON.new(dir)
194
+ when "oj"
195
+ DocDB::Oj.new(dir)
196
+ else
197
+ DocDB::Marshal.new(dir)
198
+ end
199
+
200
+ @frontmatter.merge!(@config["default_meta"]) if @config["default_meta"]
201
+
202
+ # Merge ACCS Frontmatter
203
+ if @accs_processing && @config["alt_frontmatter"]
204
+ @frontmatter.merge!(@config["alt_frontmatter"])
205
+ end
206
+
207
+ unless File.exist? outdir
208
+ STDERR.puts "destination directory is not exist. creating (only one step.)"
209
+ FileUtils.mkdir_p outdir
210
+ end
211
+ end
212
+
213
+ def initialize(config)
214
+ @config = config
215
+ @docobject = {}
216
+ @this_time_processed = []
217
+
218
+ # --metadata-file
219
+ @frontmatter = {}
220
+
221
+ @refresh = false # Force generate all documents.
222
+ @skip_index = false # Don't register to index.
223
+ @outfile = nil # Fixed output filename
224
+ @add_meta = nil
225
+ @accs = nil
226
+ @accs_index = {}
227
+ @now = Time.now
228
+ end
229
+
230
+ # Process command-line
231
+ def treat_cmdline(dir=nil)
232
+ # Options definition.
233
+ opts = OptionParser.new
234
+ opts.on("-f", "--force-refresh") { @refresh = true }
235
+ opts.on("-X", "--ignore-ext") { @ignore_ext = true }
236
+ opts.on("-I", "--skip-index") { @skip_index = true }
237
+ opts.on("-o FILE", "--output") {|v| @outfile = v }
238
+ opts.on("-m FILE", "--additional-metafile") {|v| @add_meta = YAML.load(File.read(v))}
239
+ opts.parse!(ARGV)
240
+
241
+ if File.exist?(".pbsimply-bless.rb")
242
+ require "./.pbsimply-bless.rb"
243
+ end
244
+
245
+ # Set target directory.
246
+ @dir = ARGV.shift unless dir
247
+ @dir ||= "."
248
+ ENV["pbsimply_subdir"] = @dir
249
+ end
250
+
251
+ # Load document index database (.indexes.${ext}).
252
+ def load_index
253
+ if @db.exist?
254
+ @indexes = @db.load
255
+ else
256
+ @indexes = Hash.new
257
+ end
258
+ @docobject[:indexes] = @indexes
259
+ end
260
+
261
+ def target_file_extensions
262
+ [".md"]
263
+ end
264
+
265
+ # Accessor reader.
266
+ def doc
267
+ @docobject
268
+ end
269
+
270
+ attr :indexes
271
+
272
+
273
+ ###############################################
274
+ # PROCESSING FUNCTIONS #
275
+ ###############################################
276
+
277
+ # Directory mode's main function.
278
+ # Read Frontmatters from all documents and proc each documents.
279
+ def proc_dir
280
+ draft_articles = []
281
+ target_docs = []
282
+ @indexes_orig = {}
283
+ STDERR.puts "in #{@dir}..."
284
+
285
+ STDERR.puts "Checking Frontmatter..."
286
+ Dir.foreach(@dir) do |filename|
287
+ next if filename == "." || filename == ".."
288
+ if filename =~ /^\./ || filename =~ /^draft-/
289
+ draft_articles.push filename.sub(/^(?:\.|draft-)/, "")
290
+ next
291
+ end
292
+ next unless File.file?([@dir, filename].join("/"))
293
+
294
+ if !@ignore_ext and not target_file_extensions.include? File.extname filename
295
+ next
296
+ end
297
+
298
+ STDERR.puts "Checking frontmatter in #{filename}"
299
+ frontmatter, pos = read_frontmatter(@dir, filename)
300
+ frontmatter = @frontmatter.merge frontmatter
301
+ frontmatter.merge!(@add_meta) if @add_meta
302
+
303
+ if frontmatter["draft"]
304
+ @indexes.delete(filename) if @indexes[filename]
305
+ draft_articles.push filename
306
+ next
307
+ end
308
+
309
+ @indexes_orig[filename] = @indexes[filename]
310
+ @indexes[filename] = frontmatter
311
+
312
+ # Push to target documents without checking modification.
313
+ target_docs.push([filename, frontmatter, pos])
314
+ end
315
+
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
324
+
325
+ # Save index.
326
+ @db.dump(@indexes) unless @skip_index
327
+
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
337
+ end
338
+
339
+ STDERR.puts "Checking modification..."
340
+
341
+ target_docs.delete_if {|filename, frontmatter, pos| !check_modify([@dir, filename], frontmatter)}
342
+
343
+ STDERR.puts "Okay, Now ready. Process documents..."
344
+
345
+ # Proccess documents
346
+ target_docs.each do |filename, frontmatter, pos|
347
+ ext = File.extname filename
348
+ @index = frontmatter
349
+ File.open(File.join(@dir, filename)) do |f|
350
+ f.seek(pos)
351
+ File.open(".current_document#{ext}", "w") {|fo| fo.write f.read}
352
+ end
353
+
354
+ STDERR.puts "Processing #{filename}"
355
+ generate(@dir, filename, frontmatter)
356
+ end
357
+
358
+ @db.dump(@indexes) unless @skip_index
359
+
360
+ post_plugins
361
+
362
+ # ACCS processing
363
+ if @accs && !target_docs.empty?
364
+ process_accs
365
+ end
366
+ end
367
+
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
382
+ end
383
+ @dir = dir
384
+ setup_config(dir)
385
+
386
+ load_index
387
+
388
+ frontmatter, pos = read_frontmatter(dir, filename)
389
+ frontmatter = @frontmatter.merge frontmatter
390
+ @index = frontmatter
391
+
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
397
+
398
+ generate(dir, filename, frontmatter)
399
+
400
+ post_plugins(frontmatter)
401
+
402
+ else
403
+ # Normal (directory) mode.
404
+ setup_config(@dir)
405
+ load_index
406
+
407
+ @accs = true if File.exist?(File.join(@dir, ".accs.yaml"))
408
+
409
+ # Check existing in indexes.
410
+ @indexes.delete_if {|k,v| ! File.exist?([@dir, k].join("/")) }
411
+
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
478
+ end
479
+ end
480
+ end
481
+
482
+ def generate(dir, filename, frontmatter)
483
+ print_fileproc_msg(filename) # at sub-class
484
+
485
+ # Preparing and pre script.
486
+ orig_filepath = [dir, filename].join("/")
487
+ ext = File.extname(filename)
488
+ procdoc = sprintf(".current_document%s", ext)
489
+ pre_plugins(procdoc, frontmatter)
490
+
491
+ doc = process_document(dir, filename, frontmatter, orig_filepath, ext, procdoc) # at sub-class
492
+
493
+ ##### Post eRuby
494
+ if @config["post_eruby"]
495
+ STDERR.puts "Porcessing with eRuby."
496
+ doc = ERB.new(doc, nil, "%<>").result(binding)
497
+ end
498
+
499
+ # 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
509
+
510
+ File.open(outpath, "w") do |f|
511
+ f.write(doc)
512
+ end
513
+
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 = YAML.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
544
+
545
+ accsmode
546
+ @dir = File.join(@dir, ".index.md")
547
+ main
548
+ end
549
+
550
+ ###############################################
551
+ # PRIVATE METHODS (treat document) #
552
+ ###############################################
553
+
554
+ private
555
+
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 = YAML.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 = YAML.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 = YAML.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
+ # Check is the article modified? (or force update?)
742
+ def check_modify(path, frontmatter)
743
+ modify = true
744
+ index = @indexes_orig[path[1]].dup || {}
745
+ frontmatter = @db.cmp_obj(frontmatter)
746
+ index.delete("_last_proced")
747
+ frontmatter.delete("_last_proced")
748
+
749
+ if index == frontmatter
750
+ STDERR.puts "#{path[1]} is not modified."
751
+ modify = false
752
+ else
753
+ STDERR.puts "#{path[1]} last modified at #{frontmatter["_mtime"]}, last processed at #{@indexes_orig[path[1]]&.[]("_last_proced") || 0}"
754
+ frontmatter["last_update"] = @now.strftime("%Y-%m-%d %H:%M:%S")
755
+ end
756
+
757
+ if @refresh
758
+ # Refresh (force update) mode.
759
+ true
760
+ else
761
+ modify
762
+ end
763
+ 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
+ end
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pbsimply
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Masaki Haruka
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-01-02 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Pre compile, static serving headless CMS
14
+ email:
15
+ - yek@reasonset.net
16
+ executables:
17
+ - pbsimply
18
+ - pbsimply-testserver
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - bin/pbsimply
23
+ - bin/pbsimply-testserver
24
+ - lib/pbsimply.rb
25
+ homepage: https://github.com/reasonset/purebuilder-simply
26
+ licenses:
27
+ - Apache-2.0
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubygems_version: 3.2.29
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: PureBuiler Simply
48
+ test_files: []