class PBSimply
Constants
- JSON_LIB
- POST_PROCESSORS
Attributes
indexes[R]
Public Class Methods
load_config()
click to toggle source
Load config file.
# File lib/pbsimply.rb, line 158 def self.load_config config = nil begin File.open(".pbsimply.yaml") do |f| config = Psych.unsafe_load(f) end rescue abort "Failed to load config file (./.pbsimply.yaml)" end # Required values config["outdir"] or abort "Output directory is not set (outdir)." config["template"] ||= "./template.html" config end
new(config)
click to toggle source
# File lib/pbsimply.rb, line 213 def initialize(config) @config = config @docobject = {} @this_time_processed = [] # --metadata-file @frontmatter = {} @refresh = false # Force generate all documents. @skip_index = false # Don't register to index. @outfile = nil # Fixed output filename @add_meta = nil @accs = nil @accs_index = {} @now = Time.now end
Public Instance Methods
doc()
click to toggle source
Accessor reader.
# File lib/pbsimply.rb, line 266 def doc @docobject end
generate(dir, filename, frontmatter)
click to toggle source
# File lib/pbsimply.rb, line 482 def generate(dir, filename, frontmatter) print_fileproc_msg(filename) # at sub-class # Preparing and pre script. orig_filepath = [dir, filename].join("/") ext = File.extname(filename) procdoc = sprintf(".current_document%s", ext) pre_plugins(procdoc, frontmatter) doc = process_document(dir, filename, frontmatter, orig_filepath, ext, procdoc) # at sub-class ##### Post eRuby if @config["post_eruby"] STDERR.puts "Porcessing with eRuby." doc = ERB.new(doc, nil, "%<>").result(binding) end # Write out outext = frontmatter["force_ext"] || ".html" outpath = case when @outfile @outfile when @accs_processing File.join(@config["outdir"], @dir, "index") + outext else File.join(@config["outdir"], @dir, File.basename(filename, ".*")) + outext end File.open(outpath, "w") do |f| f.write(doc) end # Mark processed @this_time_processed.push({source: orig_filepath, dest: outpath}) end
load_index()
click to toggle source
Load document index database (.indexes.${ext}).
# File lib/pbsimply.rb, line 252 def load_index if @db.exist? @indexes = @db.load else @indexes = Hash.new end @docobject[:indexes] = @indexes end
main()
click to toggle source
Run PureBuilder Simply.
# File lib/pbsimply.rb, line 369 def main # If target file is regular file, run as single mode. @singlemode = true if File.file?(@dir) # Check single file mode. if @singlemode # Single file mode if @dir =~ %r:(.*)/([^/]+): dir = $1 filename = $2 else dir = "." filename = @dir end @dir = dir setup_config(dir) load_index frontmatter, pos = read_frontmatter(dir, filename) frontmatter = @frontmatter.merge frontmatter @index = frontmatter ext = File.extname filename File.open(File.join(dir, filename)) do |f| f.seek(pos) File.open(".current_document#{ext}", "w") {|fo| fo.write f.read} end generate(dir, filename, frontmatter) post_plugins(frontmatter) else # Normal (directory) mode. setup_config(@dir) load_index @accs = true if File.exist?(File.join(@dir, ".accs.yaml")) # Check existing in indexes. @indexes.delete_if {|k,v| ! File.exist?([@dir, k].join("/")) } proc_dir end ensure # Clean up temporary files. Dir.children(".").each do |fn| if fn[0, 22] == ".pbsimply-defaultfiles.yaml" or fn[0, 21] == ".pbsimply-frontmatter" or fn[0, 17] == ".current_document" File.delete fn end end end
post_plugins(frontmatter=nil)
click to toggle source
# File lib/pbsimply.rb, line 449 def post_plugins(frontmatter=nil) if File.directory?(".post_generate") STDERR.puts("Processing with post plugins") @this_time_processed.each do |v| STDERR.puts "Processing #{v[:dest]} (from #{v[:source]})" procdoc = v[:dest] frontmatter ||= @indexes[File.basename v[:source]] File.open(".pbsimply-frontmatter.json", "w") {|f| f.write JSON_LIB.dump(frontmatter)} Dir.entries(".post_generate").sort.each do |script_file| next if script_file =~ /^\./ STDERR.puts "Running script: #{script_file}" script_file = File.join(".post_generate", script_file) post_script_result = nil script_cmdline = case when File.executable?(script_file) [script_file, procdoc] when POST_PROCESSORS[File.extname(script_file)] [POST_PROCESSORS[File.extname(script_file)], script_file, procdoc] else ["perl", script_file, procdoc] end IO.popen({"pbsimply_frontmatter" => ".pbsimply-frontmatter.json", "pbsimply_indexes" => @db.path}, script_cmdline) do |io| post_script_result = io.read end File.open(procdoc, "w") {|f| f.write post_script_result} end end end end
pre_plugins(procdoc, frontmatter)
click to toggle source
# File lib/pbsimply.rb, line 425 def pre_plugins(procdoc, frontmatter) if File.directory?(".pre_generate") STDERR.puts("Processing with pre plugins") script_file = File.join(".pre_generate", script_file) Dir.entries(".pre_generate").sort.each do |script_file| next if script_file =~ /^\./ STDERR.puts "Running script: #{File.basename script_file}" pre_script_result = nil script_cmdline = case when File.executable?(script_file) [script_file, procdoc] when POST_PROCESSORS[File.extname(script_file)] [POST_PROCESSORS[File.extname(script_file)], script_file, procdoc] else ["perl", script_file, procdoc] end IO.popen({"pbsimply_doc_frontmatter" => YAML.dump(frontmatter)}, script_cmdline) do |io| pre_script_result = io.read end File.open(procdoc, "w") {|f| f.write pre_script_result} end end end
proc_dir()
click to toggle source
Directory mode’s main function. Read Frontmatters from all documents and proc each documents.
# File lib/pbsimply.rb, line 279 def proc_dir draft_articles = [] target_docs = [] @indexes_orig = {} STDERR.puts "in #{@dir}..." STDERR.puts "Checking Frontmatter..." Dir.foreach(@dir) do |filename| next if filename == "." || filename == ".." if filename =~ /^\./ || filename =~ /^draft-/ draft_articles.push filename.sub(/^(?:\.|draft-)/, "") next end next unless File.file?([@dir, filename].join("/")) if !@ignore_ext and not target_file_extensions.include? File.extname filename next end STDERR.puts "Checking frontmatter in #{filename}" frontmatter, pos = read_frontmatter(@dir, filename) frontmatter = @frontmatter.merge frontmatter frontmatter.merge!(@add_meta) if @add_meta if frontmatter["draft"] @indexes.delete(filename) if @indexes[filename] draft_articles.push filename next end @indexes_orig[filename] = @indexes[filename] @indexes[filename] = frontmatter # Push to target documents without checking modification. target_docs.push([filename, frontmatter, pos]) end # Delete turn to draft article. draft_articles.each do |df| STDERR.puts "#{df} was turn to draft. deleting..." [df, (df + ".html"), File.basename(df, ".*"), (File.basename(df, ".*") + ".html")].each do |tfn| tfp = File.join(@config["outdir"], @dir, tfn) File.delete tfp if File.file?(tfp) end end # Save index. @db.dump(@indexes) unless @skip_index STDERR.puts "Blessing..." # Modify frontmatter `BLESSING` target_docs.each do |filename, frontmatter, pos| if @config["bless_style"] == "cmd" bless_cmd(frontmatter) else bless_ruby(frontmatter) end end STDERR.puts "Checking modification..." target_docs.delete_if {|filename, frontmatter, pos| !check_modify([@dir, filename], frontmatter)} STDERR.puts "Okay, Now ready. Process documents..." # Proccess documents target_docs.each do |filename, frontmatter, pos| ext = File.extname filename @index = frontmatter File.open(File.join(@dir, filename)) do |f| f.seek(pos) File.open(".current_document#{ext}", "w") {|fo| fo.write f.read} end STDERR.puts "Processing #{filename}" generate(@dir, filename, frontmatter) end @db.dump(@indexes) unless @skip_index post_plugins # ACCS processing if @accs && !target_docs.empty? process_accs end end
process_accs()
click to toggle source
letsaccs
This method called on the assumption that processed all documents and run as directory mode.
# File lib/pbsimply.rb, line 521 def process_accs STDERR.puts "Processing ACCS index..." if File.exist?(File.join(@dir, ".accsindex.erb")) erbtemplate = File.read(File.join(@dir, ".accsindex.erb")) elsif File.exist?(".accsindex.erb") erbtemplate = File.read(".accsindex.erb") else erbtemplate = ACCS::INDEX end # Get infomation @accs_index = Psych.unsafe_load(File.read([@dir, ".accs.yaml"].join("/"))) @accs_index["title"] ||= (@config["accs_index_title"] || "Index") @accs_index["date"] ||= Time.now.strftime("%Y-%m-%d") @accs_index["pagetype"] = "accs_index" @index = @frontmatter.merge @accs_index doc = ERB.new(erbtemplate, trim_mode: "%<>").result(binding) File.open(File.join(@dir, ".index.md"), "w") do |f| f.write doc end accsmode @dir = File.join(@dir, ".index.md") main end
setup_config(dir)
click to toggle source
initialize phase,
# File lib/pbsimply.rb, line 176 def setup_config(dir) ENV["pbsimply_outdir"] = @config["outdir"] @docobject[:config] = @config if @singlemode outdir = [@config["outdir"], @dir.sub(%r:/[^/]*$:, "")].join("/") else outdir = [@config["outdir"], @dir].join("/") end p dir # Format for Indexes database @db = case @config["dbstyle"] when "yaml" DocDB::YAML.new(dir) when "json" DocDB::JSON.new(dir) when "oj" DocDB::Oj.new(dir) else DocDB::Marshal.new(dir) end @frontmatter.merge!(@config["default_meta"]) if @config["default_meta"] # Merge ACCS Frontmatter if @accs_processing && @config["alt_frontmatter"] @frontmatter.merge!(@config["alt_frontmatter"]) end unless File.exist? outdir STDERR.puts "destination directory is not exist. creating (only one step.)" FileUtils.mkdir_p outdir end end
target_file_extensions()
click to toggle source
# File lib/pbsimply.rb, line 261 def target_file_extensions [".md"] end
treat_cmdline(dir=nil)
click to toggle source
Process command-line
# File lib/pbsimply.rb, line 231 def treat_cmdline(dir=nil) # Options definition. opts = OptionParser.new opts.on("-f", "--force-refresh") { @refresh = true } opts.on("-X", "--ignore-ext") { @ignore_ext = true } opts.on("-I", "--skip-index") { @skip_index = true } opts.on("-o FILE", "--output") {|v| @outfile = v } opts.on("-m FILE", "--additional-metafile") {|v| @add_meta = Psych.unsafe_load(File.read(v))} opts.parse!(ARGV) if File.exist?(".pbsimply-bless.rb") require "./.pbsimply-bless.rb" end # Set target directory. @dir = ARGV.shift unless dir @dir ||= "." ENV["pbsimply_subdir"] = @dir end
Private Instance Methods
accsmode()
click to toggle source
Turn on ACCS
processing mode.
# File lib/pbsimply.rb, line 557 def accsmode @accs_processing = true @singlemode = true @skip_index = true end
autobless(frontmatter)
click to toggle source
Blessing automatic method with configuration.
# File lib/pbsimply.rb, line 818 def autobless(frontmatter) catch(:accs_rel) do # find Next/Prev page on accs if @accs && @config["blessmethod_accs_rel"] # Preparing. Run at once. if !@article_order @rev_article_order_index = {} case @config["blessmethod_accs_rel"] when "numbering" @article_order = @indexes.to_a.sort_by {|i| i[1]["_filename"].to_i } when "date" begin @article_order = @indexes.to_a.sort_by {|i| i[1]["date"]} rescue abort "*** Automatic Blessing Method Error: Maybe some article have no date." end when "timestamp" begin @article_order = @indexes.to_a.sort_by {|i| i[1]["timestamp"]} rescue abort "*** Automatic Blessing Method Error: Maybe some article have no timetsamp." end when "lexical" @article_order = @indexes.to_a.sort_by {|i| i[1]["_filename"]} end @article_order.each_with_index {|x,i| @rev_article_order_index[x[0]] = i } end throw(:accs_rel) unless index = @rev_article_order_index[frontmatter["_filename"]] if @article_order[index + 1] frontmatter["next_article"] = {"url" => @article_order[index + 1][1]["page_url"], "title" => @article_order[index + 1][1]["title"]} end if index > 0 frontmatter["prev_article"] = {"url" => @article_order[index - 1][1]["page_url"], "title" => @article_order[index - 1][1]["title"]} end end end end
bless_cmd(frontmatter)
click to toggle source
# File lib/pbsimply.rb, line 801 def bless_cmd(frontmatter) File.open(".pbsimply-frontmatter.json", "w") {|f| f.write JSON_LIB.dump(frontmatter) } # BLESSING (Always) if @config["bless_cmd"] (Array === @config["bless_cmd"] ? system(*@config["bless_cmd"]) : system(@config["bless_cmd"]) ) or abort "*** BLESS COMMAND RETURNS NON-ZERO STATUS" end # BLESSING (ACCS) if @config["bless_accscmd"] (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" end mod_frontmatter = JSON.load(File.read(".pbsimply-frontmatter.json")) frontmatter.replace(mod_frontmatter) autobless(frontmatter) end
bless_ruby(frontmatter)
click to toggle source
# File lib/pbsimply.rb, line 765 def bless_ruby(frontmatter) # BLESSING (Always) if PureBuilder.const_defined?(:BLESS) && Proc === PureBuilder::BLESS begin PureBuilder::BLESS.(frontmatter, self) rescue STDERR.puts "*** BLESSING PROC ERROR ***" raise end end # BLESSING (ACCS) if @accs && PureBuilder::ACCS.const_defined?(:BLESS) && Proc === PureBuilder::ACCS::BLESS begin PureBuilder::ACCS::BLESS.(frontmatter, self) rescue STDERR.puts "*** ACCS BLESSING PROC ERROR ***" raise end end # ACCS DEFINITIONS if @accs if Proc === PureBuilder::ACCS::DEFINITIONS[:next] i = PureBuilder::ACCS::DEFINITIONS[:next].call(frontmatter, self) frontmatter["next_article"] = i if i end if Proc === PureBuilder::ACCS::DEFINITIONS[:prev] i = PureBuilder::ACCS::DEFINITIONS[:prev].call(frontmatter, self) frontmatter["prev_article"] = i if i end end autobless(frontmatter) end
check_modify(path, frontmatter)
click to toggle source
Check is the article modified? (or force update?)
# File lib/pbsimply.rb, line 742 def check_modify(path, frontmatter) modify = true index = @indexes_orig[path[1]].dup || {} frontmatter = @db.cmp_obj(frontmatter) index.delete("_last_proced") frontmatter.delete("_last_proced") if index == frontmatter STDERR.puts "#{path[1]} is not modified." modify = false else STDERR.puts "#{path[1]} last modified at #{frontmatter["_mtime"]}, last processed at #{@indexes_orig[path[1]]&.[]("_last_proced") || 0}" frontmatter["last_update"] = @now.strftime("%Y-%m-%d %H:%M:%S") end if @refresh # Refresh (force update) mode. true else modify end end
read_frontmatter(dir, filename)
click to toggle source
Read Frontmatter from the document. This method returns frontmatter, pos. pos means position at end of Frontmatter on the file.
# File lib/pbsimply.rb, line 566 def read_frontmatter(dir, filename) frontmatter = nil pos = nil if File.exist? File.join(dir, ".meta." + filename) # Load standalone metadata YAML. frontmatter = Psych.unsafe_load(File.read(File.join(dir, (".meta." + filename)))) pos = 0 else case File.extname filename when ".md" # Load Markdown's YAML frontmatter. File.open(File.join(dir, filename)) do |f| l = f.gets next unless l && l.chomp == "---" lines = [] while l = f.gets break if l.nil? break if l.chomp == "---" lines.push l end next if f.eof? begin frontmatter = Psych.unsafe_load(lines.join) rescue => e STDERR.puts "!CRITICAL: Cannot parse frontmatter." raise e end pos = f.pos end when ".rst" # ReSTRUCTURED Text File.open(File.join(dir, filename)) do |f| l = f.gets if l =~ /:([A-Za-z_-]+): (.*)/ #docinfo frontmatter = { $1 => [$2.chomp] } last_key = $1 # Read docinfo while(l = f.gets) break if l =~ /^\s*$/ # End of docinfo if l =~ /^\s+/ # Continuous line docinfo_lines.last.push($'.chomp) elsif l =~ /:([A-Za-z_-]+): (.*)/ frontmatter[$1] = [$2.chomp] last_key = $1 end end # Treat docinfo lines frontmatter.each do |k,v| v = v.join(" ") #if((k == "author" || k == "authors") && v.include?(";")) # Multiple authors. if(v.include?(";")) # Multiple element. v = v.split(/\s*;\s*/) elsif k == "date" # Date? # Datetime? if v =~ /[0-2][0-9]:[0-6][0-9]/ v = DateTime.parse(v) else v = Date.parse(v) end elsif v == "yes" || v == "true" v = true else # Simple String. nil # keep v end frontmatter[k] = v end elsif l && l.chomp == ".." #YAML # Load ReST YAML that document begins comment and block is yaml. lines = [] while(l = f.gets) if(l !~ /^\s*$/ .. l =~ /^\s*$/) if l=~ /^\s*$/ break else lines.push l end end end next if f.eof? # Rescue for failed to read YAML. begin frontmatter = Psych.unsafe_load(lines.map {|i| i.sub(/^\s*/, "") }.join) rescue STDERR.puts "Error in parsing ReST YAML frontmatter (#{$!})" next end else next end pos = f.pos end end end abort "This document has no frontmatter" unless frontmatter abort "This document has no title." unless frontmatter["title"] ### Additional meta values. ### frontmatter["source_directory"] = dir # Source Directory frontmatter["source_filename"] = filename # Source Filename frontmatter["source_path"] = File.join(dir, filename) # Source Path # URL in site. this_url = (File.join(dir, filename)).sub(/^[\.\/]*/) { @config["self_url_prefix"] || "/" }.sub(/\.[a-zA-Z0-9]+$/, ".html") frontmatter["page_url"] = this_url # URL in site with URI encode. frontmatter["page_url_encoded"] = ERB::Util.url_encode(this_url) 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")) frontmatter["page_html_escaped"] = ERB::Util.html_escape(this_url) 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")) # Title with URL Encoded. frontmatter["title_encoded"] = ERB::Util.url_encode(frontmatter["title"]) frontmatter["title_html_escaped"] = ERB::Util.html_escape(frontmatter["title"]) fts = frontmatter["timestamp"] fts = fts.to_datetime if Time === fts if DateTime === fts frontmatter["timestamp_xmlschema"] = fts.xmlschema frontmatter["timestamp_jplocal"] = fts.strftime('%Y年%m月%d日 %H時%M分%S秒') frontmatter["timestamp_rubytimestr"] = fts.strftime('%a %b %d %H:%M:%S %Z %Y') frontmatter["timestamp_str"] = fts.strftime("%Y-%m-%d %H:%M:%S %Z") elsif Date === fts frontmatter["timestamp_xmlschema"] = fts.xmlschema frontmatter["timestamp_jplocal"] = fts.strftime('%Y年%m月%d日') frontmatter["timestamp_rubytimestr"] = fts.strftime('%a %b %d') frontmatter["timestamp_str"] = fts.strftime("%Y-%m-%d") elsif Date === frontmatter["Date"] fts = frontmatter["Date"] frontmatter["timestamp_xmlschema"] = fts.xmlschema frontmatter["timestamp_jplocal"] = fts.strftime('%Y年%m月%d日') frontmatter["timestamp_rubytimestr"] = fts.strftime('%a %b %d') frontmatter["timestamp_str"] = fts.strftime("%Y-%m-%d") end fsize = FileTest.size(File.join(dir, filename)) mtime = File.mtime(File.join(dir, filename)).to_i frontmatter["_filename"] ||= filename frontmatter["pagetype"] ||= "post" frontmatter["_size"] = fsize frontmatter["_mtime"] = mtime frontmatter["_last_proced"] = @now.to_i if File.extname(filename) == ".md" frontmatter["_docformat"] = "Markdown" elsif File.extname(filename) == ".rst" || File.extname(filename) == ".rest" frontmatter["_docformat"] = "ReST" end frontmatter["date"] ||= @now.strftime("%Y-%m-%d %H:%M:%S") return frontmatter, pos end