pbsimply 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []