lib/legal_markdown/legal_to_markdown/leaders.rb
module LegalToMarkdown
extend self
module Leaders
def run_leaders
get_the_substitutions
unless @substitutions == {}
find_the_block
if @block
chew_on_the_block
clean_up_leaders
end
end
end
def get_the_substitutions
# find the headers in the remaining YAML
# parse out the headers into level-X and pre-X headers
# then combine them into a coherent package
# returns a hash with the keys as the l., ll. searches
# and the values as the replacements in the form of
# an array where the first value is a symbol and the
# second value is the precursor
#
# @substitutions hash example
# {"ll." || "l2."=>[:type8, "Article ", "(", "1", ")", :no_reset || nil, " ", :preval || :pre || nil]}
@substitutions = {}
get_level_style
get_the_indents
get_the_levels
get_the_resets
end
def find_the_block
block_pattern = /(^```+\s*\n?)(.*?\n?)(^```+\s*\n?|\z)/m
parts = @content.partition( block_pattern )
if parts[1] != ""
@block = $2.chomp
@content = parts[0] + "{{block}}" + parts[2]
else
@block = nil
@content = @content
end
end
def chew_on_the_block
# @substitutions hash example
# {"ll."OR "l2."=>[:type8, "Article ", "(", "1", ")", :no_reset || nil, :no_indent || nil, :preval || :pre || nil],}
@cross_references = {}
arrayed_block = []
@block.each_line do |line|
next if line[/^\s+$/]
line[/(^l+\.|^l\d\.)\s*(\|.*?\|)*\s*(.*)$/] ? arrayed_block << [$1, $3, $2] : arrayed_block.last[1] << ("\n" + line.rstrip)
end
@block = build_the_block_for_markdown arrayed_block if @writer == :markdown
@block = build_the_block_for_jason arrayed_block if @writer == :jason
end
def clean_up_leaders
@content.gsub!("{{block}}", @block ) if @writer == :markdown
if @writer == :jason
@content = @content.partition( /\{\{block\}\}/ )
@content[1] = @block
end
@block = ""
end
private
def get_level_style
begin
@headers["level-style"] =~ /l1/ ? @deep_leaders = true : @deep_leaders = false
@headers.delete("level-style")
rescue
@deep_leaders = false
end
end
def get_the_indents
begin
no_indent_array = @headers["no-indent"].split(", ")
no_indent_array.include?("l." || "l1.") ? @offset = no_indent_array.size : @offset = no_indent_array.size + 1
@headers.delete("no-indent")
rescue
@offset = 1
end
end
def get_the_levels
begin
@headers.each do | header, value |
if @deep_leaders
search = "l" + header[-1] + "." if header =~ /level-\d/
else
search = "l" * header[-1].to_i + "." if header =~ /level-\d/
end
if header =~ /level-\d/
@substitutions[search]= set_the_subs_arrays(value.to_s)
@deep_leaders ? spaces = (search[1].to_i - @offset) : spaces = (search.size - @offset - 1)
spaces < 0 ? spaces = 0 : spaces = spaces * 2
@substitutions[search][6] = " " * spaces
if value =~ /\s*preval\s*/
@substitutions[search][1].gsub!(/preval\s*/, "")
@substitutions[search][7] = :preval
elsif value =~ /\s*pre\s*/
@substitutions[search][1].gsub!(/pre\s*/, "")
@substitutions[search][7] = :pre
end
@headers.delete(header)
end
end
rescue
@substitutions = {}
end
end
def get_the_resets
begin
no_subs_array = @headers["no-reset"].split(", ")
no_subs_array.each{ |e| @substitutions[e][5] = :no_reset unless e == "l." || e == "l1."}
@headers.delete("no-reset")
rescue
end
end
def set_the_subs_arrays( value )
# takes a core value from the hash pulled from the yaml
# returns an array with a type symbol and a precursor string
case
when value =~ /([IVXLCDM]+)\.\z/ # type1 : {{ I. }}
return[:type1, value.delete($1 + "."), "", $1, "."]
when value =~ /\(([IVXLCDM]+)\)\z/ # type2 : {{ (I) }}
return[:type2, value.delete("(" + $1 + ")"), "(", $1, ")"]
when value =~ /([ivxlcdm]+)\.\z/ # type3 : {{ i. }}
return[:type3, value.delete($1 + "."), "", $1, "."]
when value =~ /\(([ivxlcdm]+)\)\z/ # type4 : {{ (i) }}
return[:type4, value.delete("(" + $1 + ")"), "(", $1, ")"]
when value =~ /([A-Z]+)\.\z/ # type5 : {{ A. }}
return[:type5, value.delete($1 + "."), "", $1, "."]
when value =~ /\(([A-Z]+)\)\z/ # type6 : {{ (A) }}
return[:type6, value.delete("(" + $1 + ")"), "(", $1, ")"]
when value =~ /([a-z]+)\.\z/ # type7 : {{ a. }}
return[:type7, value.delete($1 + "."), "", $1, "."]
when value =~ /\(([a-z]+)\)\z/ # type8 : {{ (a) }}
return[:type8, value.delete("(" + $1 + ")"), "(", $1, ")"]
when value =~ /\((\d+)\)\z/ # type9 : {{ (1) }}
return[:type9, value.delete("(" + $1 + ")"), "(", $1, ")"]
else value =~ /(\d+)\.\z/ # type0 : {{ 1. }} ... also default
return[:type0, value.delete($1 + "."), "", $1, "."]
end
end
def romans_takedown( array_to_sub )
if array_to_sub[0] == :type1 || array_to_sub[0] == :type2
@r_u = true
elsif array_to_sub[0] == :type3 || array_to_sub[0] == :type4
@r_l = true
end
if @r_l || @r_u
array_to_sub[3] = RomanNumerals.to_decimal_string(array_to_sub[3])
end
return array_to_sub
end
def romans_setup( array_to_sub )
if @r_l || @r_u
array_to_sub[3] = RomanNumerals.to_roman_upper(array_to_sub[3]) if @r_u
array_to_sub[3] = RomanNumerals.to_roman_lower(array_to_sub[3]) if @r_l
end
@r_l = false; @r_u = false
return array_to_sub
end
def increment_the_branch( array_to_sub, selector, next_selector )
if selector > next_selector #going up the tree and reset
selectors_to_reset = @substitutions.inject([]){ |m,(k,v)| m << k if k > next_selector; m }
selectors_to_reset.each do | this_selector |
substitutor = @substitutions[this_selector]
substitutor = romans_takedown( substitutor )
substitutor[3].next! if this_selector == selector
if substitutor[0] == :type5 || substitutor[0] == :type6
substitutor[3] = "A" unless substitutor[5] == :no_reset
elsif substitutor[0] == :type7 || substitutor[0] == :type8
substitutor[3] = "a" unless substitutor[5] == :no_reset
else
substitutor[3] = "1" unless substitutor[5] == :no_reset
end
substitutor = romans_setup( substitutor )
@substitutions[this_selector]= substitutor
end
array_to_sub = @substitutions[selector]
else #not going up tree
array_to_sub = romans_takedown( array_to_sub )
array_to_sub[3].next!
array_to_sub = romans_setup( array_to_sub )
end
return array_to_sub
end
def get_selector_above( selector )
if @deep_leaders
selector_above = "l" + (selector[1].to_i-1).to_s + "."
selector_above = "l1." if selector_above == "l0."
else
selector_above = selector[1..-1]
selector_above = "l." if selector_above == "."
end
return selector_above
end
def find_parent_reference( selector_above )
leading_prov = @substitutions[selector_above].clone
leading_prov = romans_takedown( leading_prov )
if leading_prov[0] == ( :type5 || :type6 || :type7 || :type8 )
leading_prov[3] = leading_prov[3][0..-2] + (leading_prov[3][-1].ord-1).chr
else
leading_prov[3] = (leading_prov[3].to_i-1).to_s
end
leading_prov = romans_setup( leading_prov )
return leading_prov
end
def preval_substitution( array_to_sub, selector )
array_to_sub.pop unless array_to_sub.last == :preval
selector_above = get_selector_above( selector )
leading_prov = find_parent_reference( selector_above )[3]
trailing_prov = array_to_sub[3].clone
trailing_prov = "0" + trailing_prov if trailing_prov.size == 1
array_to_sub << array_to_sub[2] + leading_prov.to_s + trailing_prov.to_s + array_to_sub[4]
array_to_sub.last.gsub!($1, "(") if array_to_sub.last[/(\.\()/]
return array_to_sub
end
def pre_substitution( array_to_sub, selector )
array_to_sub.pop unless array_to_sub.last == :pre
selector_above = get_selector_above( selector )
leading_prov = @substitutions[selector_above][8] || find_parent_reference( selector_above )[2..4].join
trailing_prov = array_to_sub[2..4].join
array_to_sub << leading_prov + trailing_prov
array_to_sub.last.gsub!($1, "(") if array_to_sub.last[/(\.\()/]
return array_to_sub
end
def log_the_line( block, sub_it, reference, arrayed_line )
arrayed_line[1].gsub!("\n", "\n\n" + sub_it[6]) if arrayed_line[1] =~ /\n/
block << sub_it[6] + sub_it[1] + reference + " " + arrayed_line[1] + "\n\n"
end
def log_the_key( block, sub_it, reference, selector, arrayed_line, arrayed_block )
# this method will build a hash which we will use to build the structured JSON.
# roughly the hash / json will break down like this ....
# "id"
# "nodes"
# "document"
# "title" => ""
# "abstract" => ""
# "views" => ["content"]
# "provision:SHA32"
# "id" => "provision:SHA32"
# "type" => "provision"
# "data"
# "level" => "level"
# "provision_reference" => "assembled leader"
# "provision_text" => "text" #gaurd italics
# "citation" => "citation" #todo
# "substitution" => sub_it array #for json=>lmd reversion
# "annotation:SHA32"
# "id" => "annotation:SHA32"
# "type" => "citation"
# "data"
# "pos" => [start_column, stop_column]
# "citation_type" => "internal" | "external"
# "cite_of" => #todo
# "node" => "id" ... if internal
# "substitution" => for json=>reversion
# "content" => "nodes" => ["provision:SHA32", ...]
# but this method is only chewing on the middle bits where it says provision || annotation.
# the json_builder module will handle the rest.
provision = { "id" => "provision:" + SecureRandom.hex, "type" => "provision" }
provision["data"]= {
"level" => @deep_leaders ? selector[-1] : (selector.length - 1).to_s,
"provision_reference" => sub_it[1] + reference,
"provision_text" => arrayed_line[1].count("*") % 2 != 0 ? arrayed_line[1].sub("*", "") : arrayed_line[1],
"citation" => "",
"substitution" => sub_it
}
return provision
end
def build_an_annotation( start, stop, cite, parent, substitution )
annotation = { "id" => "annotation:" + SecureRandom.hex, "type" => "citation"}
annotation["data"]= {
"pos" => [start, stop],
"citation_type" => "internal",
"cite_of" => cite,
"node" => parent,
"substitution" => substitution
}
return annotation
end
def block_builder( arrayed_line )
selector = arrayed_line.first
sub_it = @substitutions[selector]
if sub_it[7] == :preval
sub_it = preval_substitution( sub_it, selector )
reference = sub_it.last
elsif sub_it[7] == :pre
sub_it = pre_substitution( sub_it, selector )
reference = sub_it.last
else
reference = sub_it[2..4].join
end
@cross_references[arrayed_line[2]]= sub_it[1].gsub(/\A\* *|\#+ */, "") + reference.chomp(".") if arrayed_line[2]
return [sub_it, reference, selector, arrayed_line]
end
def block_incrementer( arrayed_line, arrayed_block, selector, sub_it )
unless arrayed_line == arrayed_block.last
next_selector = arrayed_block[arrayed_block.index(arrayed_line)+1].first
@substitutions[selector]= increment_the_branch(sub_it, selector, next_selector)
end
end
def build_the_block_for_markdown( arrayed_block )
new_block = arrayed_block.inject("") do |block, arrayed_line|
(sub_it, reference, selector, arrayed_line) = block_builder arrayed_line
log_the_line block, sub_it, reference, arrayed_line
block_incrementer arrayed_line, arrayed_block, selector, sub_it
block
end
@cross_references.each_key{|k| new_block.gsub!(k, @cross_references[k]) }
new_block
end
def build_the_block_for_jason( arrayed_block )
require 'securerandom'
annotations_hash = {}
provisions_hash = arrayed_block.inject({}) do |block, arrayed_line|
(sub_it, reference, selector, arrayed_line) = block_builder arrayed_line
provision = log_the_key block, sub_it, reference, selector, arrayed_line, arrayed_block
block_incrementer arrayed_line, arrayed_block, selector, sub_it
block[provision["id"]]= provision
block
end
provisions_hash.each_value do |h|
if h["data"]["provision_text"][/(\|.*?\|)/]
sub = $1
ref = @cross_references[sub]
h["data"]["provision_text"].gsub!(sub, ref)
start = h["data"]["provision_text"].index(ref) + 1
stop = start + ref.length
ref = ref + "." unless ref[/\)\z/]
cite = provisions_hash.each_value.detect{|h| h["data"]["provision_reference"] == ref}["id"]
parent = h["id"]
annotation = build_an_annotation start, stop, cite, parent, sub
annotations_hash[annotation["id"]]= annotation
else
next
end
end
provisions_hash.merge(annotations_hash)
end
end
end