lib/legal_markdown/make_yaml_frontmatter.rb
#! ruby
require 'yaml'
class MakeYamlFrontMatter
def initialize(args)
@input_file = args[-2] ? args[-2] : args[-1]
@output_file = args[-1]
find_yaml_if_yaml load
scan_and_filter_yaml
build_new_yaml_frontmatter unless @yaml_data_as_array == [{},{},{},{},{}]
write_it
end
private
def load
begin
source_file = @input_file == "-" ? STDIN.read : File::read(@input_file)
source_file = guard_partials_start source_file
if source_file[1] == "\u0000" then source_file = source_file.force_encoding('utf-16le').encode('utf-8') end
source_file.gsub!("\xEF\xBB\xBF".force_encoding("UTF-8"), '')
source_file.gsub!("\xC3\xAF\xC2\xBB\xC2\xBF".force_encoding("UTF-8"), '')
source_file.gsub!("\r", '')
source_file.force_encoding("UTF-8")
source_file
rescue
puts "Sorry, I could not read the input file #{@input_file}."
exit 0
end
end
def find_yaml_if_yaml source
yaml_pattern = /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
parts = source.partition yaml_pattern
if ! parts[1].empty?
parts[1] = guard_strings parts[1]
@headers = YAML.load parts[1]
@content = parts[2]
else
@headers = {}
@content = source
end
end
def scan_and_filter_yaml
mixin_pattern = /[^\[]{{(\S+)}}/
opt_clauses_pattern = /\[{{(\S+)}}/
@structured_headers_pattern = /(^l+\.|^l\d+\.)/
@yaml_data_as_array = []
@yaml_data_as_array << ( filter_yaml mixin_pattern || {} )
@yaml_data_as_array << ( filter_yaml opt_clauses_pattern || {} )
@yaml_data_as_array << ( filter_yaml @structured_headers_pattern || {} )
@yaml_data_as_array << ( @yaml_data_as_array.last.empty? ? {} : filter_yaml(%w{no-indent no-reset level-style}) )
@yaml_data_as_array << ( filter_meta )
end
def build_new_yaml_frontmatter
@content = "\n---\n\n" + @content
replacers = {0=>"Mixins", 1=>"Optional Clauses", 2=>"Structured Headers", 3=>"Properties", 4=>"Document Meta-Data"}
stringy = @yaml_data_as_array.inject("") do |string, section|
unless section.empty?
string << "\n\# " + replacers[@yaml_data_as_array.index(section)] + "\n"
unless @yaml_data_as_array.index(section) == 4
string << sink_it(section)
else
string << YAML.dump(section).gsub(/---\s*\n/, '').gsub(/\s+\n/, "\n")
end
end
string
end
@content = stringy + @content
@content = "---\n" + @content
end
def write_it
guard_partials_finish
if @output_file && @output_file != "-"
File.open(@output_file, "w") {|f| f.write( @content ); f.close }
else
STDOUT.write @content
end
end
def filter_yaml pattern
# @headers will be a hash, stuff is an array, returns a filtered hash
stuff = pattern.is_a?(Regexp) ? scan_doc(pattern) : pattern
if stuff
stuff_in_yaml = stuff.inject({}) do |hash, elem|
@headers.has_key?(elem) ? hash.merge({elem => @headers[elem]}) : hash.merge({elem => ""})
end
end
end
def filter_meta
meta_tags = %w(meta meta-yaml-output meta-json-output meta-no-output)
meta_in_yaml = meta_tags.inject({}) do |hash, elem|
if @headers.has_key?(elem)
hash[elem] = @headers[elem]
end
hash
end
meta_in_yaml
end
def scan_doc pattern
found = @content.scan(pattern).uniq.sort.flatten
if pattern == @structured_headers_pattern
found = convert_ll_to_level_two found
end
found
end
def convert_ll_to_level_two levels
# receives an array in form ["l.", "ll.", "lll."] returns array in form ["level-1", "level-2"]
levels.inject([]) do |arr, level|
level[/((l+)\.)|(l(\d+)\.*)/]
if $2
arr << "level-" + $2.length.to_s
else
arr << "level-" + $&.delete("l").delete(".")
end
end
end
def sink_it section
section.inject("") do |string, head|
string << head[0] + ": \"" + ( head[1].to_s.gsub("\"", "\\\"") || "" ) + "\"\n"
string
end
end
def guard_partials_start source_file
source_file.scan(/(@include (.+)$)/).each do |set|
partial_file = set[1]
to_replace = set[0]
partial_contents = File::read(partial_file) if File::exists?(partial_file) && File::readable?(partial_file)
source_file.gsub!(to_replace, "[PARTIALSTART]\n" + partial_contents + "\n[PARTIALENDS][#{to_replace}]")
end
source_file
end
def guard_strings strings
strings.scan(/\:(\S.*)$/){ |m| strings = strings.gsub( m[0], " " + m[0] ) }
strings.scan(/^((level-\d+:)(.+)\"*)$/) do |m|
line = m[0]; level = m[1]; field = m[2].strip.delete "\""
if field =~ /[^\.\)]\z/
strings = strings.gsub(line, level + " \"" + field + ".\"")
else
strings = strings.gsub(line, level + " \"" + field + "\"")
end
end
strings
end
def guard_partials_finish
@content.scan(/(\[PARTIALSTART\].*?\[PARTIALENDS\]\[(.*?)\])/m).each do |set|
replacer = set[1]
to_replace = set[0]
@content.gsub!(to_replace, replacer)
end
end
end