legal_markdown 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -2,5 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in legal_markdown.gemspec
4
4
  gemspec
5
-
6
5
  gem "rake"
data/README.md CHANGED
@@ -88,8 +88,10 @@ Then you can describe the functionality that you require in the YAML front-matte
88
88
  4. `level-1: (A)` will format the same as the above only with the capital letters in a parentetical;
89
89
  5. `level-1: a.` will format with lowercase letters followed by a period;
90
90
  6. `level-1: (a)` will format with lowercase letters within a parentethical;
91
- 7. `level-1: I.` will format with Roman numerals followed by a period;
92
- 8. `level-1: (I)` will format with Roman numerals within a parententical.
91
+ 7. `level-1: I.` will format with capital Roman numerals followed by a period;
92
+ 8. `level-1: (I)` will format with capital Roman numerals within a parententical;
93
+ 9. `level-1: i.` will format with lowercase Roman numerals followed by a period;
94
+ 10. `level-1: (i)` will format with lowercase Roman numerals within a parententical..
93
95
 
94
96
  Obviously you will replace `level-1` with `level-2`, etc. Although this functionality was built into the gem, it is generally not the best practice. A better practice is to let the gem make the replacements and reformat the markdown and then use your rendering system and its default reference documents to set the styles you need.
95
97
 
@@ -152,6 +154,25 @@ In Libreoffice you would modify your template reference.odt as you need it. You
152
154
 
153
155
  Then you would save the reference.odt as a new name perhaps contract-reference.odt in your Pandoc reference folder.
154
156
 
157
+ ### Step 3(a): Add Precursors to Headers
158
+
159
+ *Note*: I only use these when I'm using pandoc to move directly to html or pdf files (a fuller suite of this is what we're working towards at Watershed, along with the fantastic team at [FrontlineSMS](http://www.frontlinesms.com/about-us/) and others hopefully). If you want to use `legal_markdown` along with .odt or .docx files probably better to utilize the workflow described *supra*.
160
+
161
+ Now that you've been warned, here's how you use precursors. Within the text of the document nothing changes. In the YAML front matter you will leave it as it was before. All you need to do is add any word or other marker before the trigger. What `legal_markdown` will do is to look at the last two characters if the marker ends in a period or three if it ends in a paren, and then everything else it will place into a precursor. If you want to reference the preceding level (like 1.1.1 in the example above) then simply put in {pre}. I'll try to make this less fragile down the road, but for now it is what it is. So, your YAML front matter will look like this:
162
+
163
+ ```yaml
164
+ ---
165
+ title: Wonderful Contract
166
+ author: Your Name
167
+ date: today
168
+ level-1: Article 1.
169
+ level-2: pre 1.
170
+ level-3: pre a.
171
+ ---
172
+ ```
173
+
174
+ The other thing you need to be aware of if you use this method is that when `legal_markdown` parses the input markdown it have to hardcode in the numbering. This means that you'll lose any features of automatic lists and list nesting which your markdown renderer may give you if you simply placed the triggers without any precursors.
175
+
155
176
  ### Step 4: Run Legal-Markdown and Pandoc
156
177
 
157
178
  Make sure when you run Pandoc you use the new reference document that you created as the basis.
@@ -170,7 +191,11 @@ Also, if you use the reference.odt or reference.docx to override the default for
170
191
 
171
192
  [ ] - Definitions. For now these can be used as mixins but that functionality needs to improve.
172
193
 
173
- [ ] - Parsing. At this point legal_markdown cannot take a markdown document and parse it to build structured headers. Legal_markdown only works with a renderer to *create* documents but not to *import* documents. I want to build this functionality out at a later date.
194
+ [ ] - Parsing. At this point legal_markdown cannot take a markdown document and parse it to build structured headers. Legal_markdown only works with a renderer to *create* documents but not to *import* documents. I want to build this functionality out at a later date. Legal_markdown is not meant as an importer for files types, there are other tools for that but I would like it to be able to parse text that is already in markdown.
195
+
196
+ [ ] - Markdown post-processing. This will cure some of the issues (class establishment and proper list nesting of structure documents) that are currently lost when using precurors.
197
+
198
+ [ ] - Different input and output files
174
199
 
175
200
  # Contributing
176
201
 
data/bin/legalmd CHANGED
@@ -4,4 +4,4 @@
4
4
  # legalmd file.md
5
5
 
6
6
  require 'legal_markdown'
7
- LegalMarkdown.execute(ARGV)
7
+ LegalMarkdown.markdown_preprocess(ARGV)
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
19
19
  s.require_paths = ["lib"]
20
20
 
21
- #s.add_dependency ""
21
+ s.add_dependency "roman-numerals"
22
22
 
23
23
  s.description = <<desc
24
24
  This gem will parse YAML Front Matter of Markdown Documents. Typically, this gem would be called with a md renderer, such as Pandoc, that would turn the md into a document such as a .pdf file or a .docx file. By combining this pre-processing with a markdown renderer, you can ensure that both the structured content and the structured styles necessary for your firm or organization are more strictly enforced. Plus you won't have to deal with Word any longer, and every lawyer should welcome that. Why? Because Word is awful.
@@ -1,3 +1,3 @@
1
1
  module LegalMarkdown
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -1,22 +1,24 @@
1
1
  #! ruby
2
2
  require 'yaml'
3
3
  require 'English'
4
+ require 'roman-numerals'
4
5
  require "legal_markdown/version"
5
6
 
6
7
  module LegalMarkdown
7
8
  extend self
8
- def execute(*args)
9
+
10
+ def markdown_preprocess(*args)
9
11
  # Get the Content & Yaml Data
10
12
  data = load(*args)
11
13
  parsed_content = parse_file(data[0])
12
14
  # Run the Mixins
13
15
  mixed_content = mixing_in(parsed_content[0], parsed_content[1])
14
- #REMOVE THESE LATER
15
- content = mixed_content[0]
16
- yaml_data = mixed_content[1]
17
- puts content
18
- puts "What's left"
19
- yaml_data.each{|k,v| puts "#{k}: #{v}"}
16
+ # Run the Pandoc Title Block
17
+ pandoc_content = pandoc_title_block( mixed_content[1] ) + mixed_content[0]
18
+ # Run the Headers
19
+ headed_content = headers_on(mixed_content[1], pandoc_content)
20
+ # Write the file
21
+ file = write_it( @filename, headed_content )
20
22
  end
21
23
 
22
24
  private
@@ -50,8 +52,9 @@ module LegalMarkdown
50
52
  # ARGV.each{ |fn| output_file(OPTS, fn) }
51
53
 
52
54
  # Load Source File
53
- source_file = File::read(ARGV[-1]) if File::exists?(ARGV[-1])
54
- return[source_file, '']
55
+ @filename = ARGV[-1]
56
+ source_file = File::read(@filename) if File::exists?(@filename) && File::readable?(@filename)
57
+ return [source_file, '']
55
58
  end
56
59
 
57
60
  # ----------------------
@@ -72,7 +75,7 @@ module LegalMarkdown
72
75
  rescue => e
73
76
  puts "Error reading file #{File.join(ARGV[0])}: #{e.message}"
74
77
  end
75
- return[data, content]
78
+ return [data, content]
76
79
  end
77
80
 
78
81
  # ----------------------
@@ -93,43 +96,256 @@ module LegalMarkdown
93
96
  end
94
97
  end
95
98
  end
96
- return[content, mixins]
99
+ return [content, mixins]
97
100
  end
98
101
 
99
102
  # ----------------------
100
103
  # | Step 4 |
101
104
  # ----------------------
102
- # Headers
103
-
105
+ # Special YAML fields
104
106
 
105
- # Step 4a: Find the block starting and finishing lines
107
+ def pandoc_title_block( headers )
108
+ title_block = ""
109
+ headers.each do | header |
110
+ if header[0].casecmp("title") == 0
111
+ title_block << "% " + header[1] + "\n"
112
+ headers.delete( header )
113
+ elsif header[0].casecmp("author") == 0
114
+ title_block << "% " + header[1] + "\n"
115
+ headers.delete( header )
116
+ elsif header[0].casecmp("date") == 0
117
+ title_block << "% " + header[1] + "\n\n"
118
+ headers.delete( header )
119
+ end
120
+ end
121
+ return title_block
122
+ end
106
123
 
124
+ # ----------------------
125
+ # | Step 5 |
126
+ # ----------------------
127
+ # Headers
107
128
 
108
- # Step 4b: Set the alignment and the nesting structure
129
+ def headers_on( headers, content )
130
+
131
+ def get_the_substitutions( headers )
132
+ # find the headers in the remaining YAML
133
+ # parse out the headers into level-X and pre-X headers
134
+ # then combine them into a coherent package
135
+ # returns a hash with the keys as the l., ll. searches
136
+ # and the values as the replacements in the form of
137
+ # an array where the first value is a symbol and the
138
+ # second value is the precursor
139
+
140
+ def set_the_subs_arrays(value)
141
+ # takes a core value from the hash pulled from the yaml
142
+ # returns an array with a type symbol and a precursor string
143
+ if value =~ /I\.\z/ # type1 : {{ I. }}
144
+ return[:type1, value[0..-3]]
145
+ elsif value =~ /\(I\)\z/ # type2 : {{ (I) }}
146
+ return[:type2, value[0..-4]]
147
+ elsif value =~ /i\.\z/ # type3 : {{ i. }}
148
+ return[:type3, value[0..-3]]
149
+ elsif value =~ /\(i\)\z/ # type4 : {{ (i) }}
150
+ return[:type4, value[0..-4]]
151
+ elsif value =~ /[A-Z]\.\z/ # type5 : {{ A. }}
152
+ return[:type5, value[0..-3]]
153
+ elsif value =~ /\([A-Z]\)\z/ # type6 : {{ (A) }}
154
+ return[:type6, value[0..-4]]
155
+ elsif value =~ /[a-z]\.\z/ # type7 : {{ a. }}
156
+ return[:type7, value[0..-3]]
157
+ elsif value =~ /\([a-z]\)\z/ # type8 : {{ (a) }}
158
+ return[:type8, value[0..-4]]
159
+ elsif value =~ /\(\d\)\z/ # type9 : {{ (1) }}
160
+ return[:type9, value[0..-3]]
161
+ else value =~ /\d\.\z/ # type0 : {{ 1. }} ... also default
162
+ return[:type0, value[0..-4]]
163
+ end
164
+ end
109
165
 
166
+ substitutions = {}
167
+ headers.each do | header, value |
168
+ if header =~ /level-\d/
169
+ level = header[-1].to_i
170
+ base = "l"
171
+ search = base * level + "."
172
+ # substitutions hash example {"ll."=>[:type8,"Article"],}
173
+ substitutions[search]= set_the_subs_arrays(value.to_s)
174
+ end
175
+ end
110
176
 
111
- # Step 4c: Set the requested list styling per the YAML front-matter
177
+ return substitutions
178
+ end
112
179
 
180
+ def find_the_block( content )
181
+ # finds the block of text that will be processed
182
+ # returns an array with the first element as the block
183
+ # to be processed and the second element as the rest of the document
184
+ if content =~ /(^l+\.\s.+^$)/m
185
+ block = $1.chomp
186
+ content = $PREMATCH + "{{block}}\n" + $POSTMATCH
187
+ end
188
+ return[ block, content ]
189
+ end
113
190
 
114
- # Step 4d: Pull out the ll. tags & strip the leading whitespace
115
- # but keep the proper nesting alignment.
191
+ def chew_on_the_block( substitutions, block )
192
+ # takes a hash of substitutions to make from the #get_the_substitutions method
193
+ # and a block of text returned from the #find_the_block method
194
+ # iterates over the block to make the appropriate substitutions
195
+ # returns a block of text
196
+
197
+ def get_the_subs_arrays( value )
198
+ # returns a new array for the replacements
199
+ if value[0] == :type1 # :type1 : {{ I. }}
200
+ return[:type1, value[1], "", "I", "."]
201
+ elsif value[0] == :type2 # :type2 : {{ (I) }}
202
+ return[:type2, value[1], "(", "I", ")"]
203
+ elsif value[0] == :type3 # :type3 : {{ i. }}
204
+ return[:type3, value[1], "", "i", "."]
205
+ elsif value[0] == :type4 # :type4 : {{ (i) }}
206
+ return[:type4, value[1], "(", "i", ")"]
207
+ elsif value[0] == :type5 # :type5 : {{ A. }}
208
+ return[:type5, value[1], "", "A", "."]
209
+ elsif value[0] == :type6 # :type6 : {{ (A) }}
210
+ return[:type6, value[1], "(", "A", ")"]
211
+ elsif value[0] == :type7 # :type7 : {{ a. }}
212
+ return[:type7, value[1], "", "a", "."]
213
+ elsif value[0] == :type8 # :type8 : {{ (a) }}
214
+ return[:type8, value[1], "(", "a", ")"]
215
+ elsif value[0] == :type9 # :type9 : {{ (1) }}
216
+ return[:type9, value[1], "(", "1", ")"]
217
+ else value[0] == :type0 # :type0 : {{ 1. }} ... also default
218
+ return[:type0, value[1], "", 1, "."]
219
+ end
220
+ end
116
221
 
222
+ def log_the_line( new_block, selector, line, array_to_sub )
223
+ substitute = array_to_sub[1..4].join
224
+ spaces = ( " " * ( (selector.size) - 1 ) * 4 )
225
+ new_block << spaces + line.gsub(selector, substitute)
226
+ end
117
227
 
118
- # ----------------------
119
- # | Step 5 |
120
- # ----------------------
121
- # Special YAML fields
228
+ def increment_the_branch( hash_of_subs, array_to_sub, selector )
229
+ romans_uppers = [ :type1, :type2 ]
230
+ romans_lowers = [ :type3, :type4 ]
231
+ romans = romans_uppers + romans_lowers
232
+ if romans.any?{ |e| e == array_to_sub[0] }
233
+ if romans_lowers.any?{ |e| e == array_to_sub[0] }
234
+ r_l = true
235
+ else
236
+ r_u = true
237
+ end
238
+ end
239
+ if r_l == true
240
+ array_to_sub[3] = array_to_sub[3].upcase
241
+ end
242
+ if r_l == true || r_u == true
243
+ array_to_sub[3] = RomanNumerals.to_decimal(array_to_sub[3])
244
+ end
245
+ array_to_sub[3] = array_to_sub[3].next
246
+ if r_l == true || r_u == true
247
+ array_to_sub[3] = RomanNumerals.to_roman(array_to_sub[3])
248
+ end
249
+ if r_l == true
250
+ array_to_sub[3] = array_to_sub[3].downcase
251
+ end
252
+ hash_of_subs[selector]= array_to_sub
253
+ return hash_of_subs
254
+ end
122
255
 
256
+ def reset_the_sub_branches( hash_of_subs, array_to_sub, selector )
257
+ hash_of_subs = increment_the_branch( hash_of_subs, array_to_sub, selector )
258
+ leaders_to_reset = []
259
+ hash_of_subs.each_key{ |k| leaders_to_reset << k if k > selector }
260
+ leaders_to_reset.each do | leader |
261
+ unless hash_of_subs[leader][5] == :pre
262
+ hash_of_subs[leader]= get_the_subs_arrays(hash_of_subs[leader])
263
+ else
264
+ hash_of_subs[leader]= get_the_subs_arrays(hash_of_subs[leader])
265
+ hash_of_subs[leader]= pre_setup(hash_of_subs[leader])
266
+ end
267
+ end
268
+ return hash_of_subs
269
+ end
123
270
 
124
- # Step 6: Strip the YAML front-matter
271
+ def pre_setup( array_to_sub )
272
+ array_to_sub[5] = :pre
273
+ array_to_sub[1] = ""
274
+ return array_to_sub
275
+ end
125
276
 
277
+ def reform_the_block( old_block, substitutions )
278
+ # method will take the old_block and iterate through the lines.
279
+ # First it will find the leading indicator. Then it will
280
+ # find the appropriate substitution from the substitutions
281
+ # hash. After that it will rebuild the leading matter from the
282
+ # sub hash. It will drop markers if it is going down the tree.
283
+ # It will reset the branches if it is going up the tree.
284
+ # sub_it is an array w/ type[0] & lead_string[1] & id's[2..4]
285
+ new_block = ""
286
+ leader_before = ""
287
+ leader_above = ""
288
+ selector_before = ""
289
+ old_block.each_line do | line |
290
+ selector = $1.chop if line =~ /(^l+.\s)/
291
+ sub_it = substitutions[selector]
292
+ if sub_it[5] == :pre || sub_it[1] =~ /pre/
293
+ sub_it = pre_setup(sub_it)
294
+ if selector_before < selector # Going down the tree, into pre
295
+ leader_above = substitutions[selector_before][1..4].join
296
+ sub_it[1] = leader_above = leader_before
297
+ elsif selector_before > selector && substitutions[selector_before][5] == :pre
298
+ sub_it[1] = leader_above[0..-3]
299
+ else
300
+ sub_it[1] = leader_above
301
+ end
302
+ end
303
+ log_the_line( new_block, selector, line, sub_it )
304
+ leader_before = sub_it[1..4].join
305
+ if selector_before > selector # We are going up the tree.
306
+ substitutions = reset_the_sub_branches(substitutions, sub_it, selector)
307
+ else # We are at the same level.
308
+ substitutions = increment_the_branch(substitutions, sub_it, selector)
309
+ end
310
+ selector_before = selector
311
+ end
312
+ return new_block
313
+ end
126
314
 
127
- # Step 7: Write the file
315
+ if block != nil && block != "" && substitutions != nil && substitutions != {}
316
+ substitutions.each_key{ |k| substitutions[k]= get_the_subs_arrays(substitutions[k]) }
317
+ new_block = reform_the_block( block, substitutions )
318
+ end
319
+ end
128
320
 
321
+ headers = get_the_substitutions( headers )
322
+ block_noted = find_the_block( content )
323
+ block = block_noted[0]
324
+ not_the_block = block_noted[1]
325
+ block_noted = "" # Really only for long documents so they don't use too much memory
129
326
 
130
- # Step 7a: Where an output file was specified
327
+ if headers == {}
328
+ block_redux = block
329
+ end
330
+ if block == nil || block == ""
331
+ block_redux = ""
332
+ else
333
+ block_redux = chew_on_the_block( headers, block )
334
+ end
131
335
 
336
+ headed = not_the_block.gsub("{{block}}", block_redux )
337
+ end
132
338
 
133
- # Step 7b: Where an output file was not specified
339
+ # ----------------------
340
+ # | Step 6 |
341
+ # ----------------------
342
+ # Write the file
134
343
 
344
+ def write_it( filename, final_content )
345
+ if File.writable?( filename )
346
+ File::open filename, 'w' do |f|
347
+ f.write final_content
348
+ end
349
+ end
350
+ end
135
351
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legal_markdown
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-18 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2013-02-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: roman-numerals
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
14
30
  description: ! " This gem will parse YAML Front Matter of Markdown Documents. Typically,
15
31
  this gem would be called with a md renderer, such as Pandoc, that would turn the
16
32
  md into a document such as a .pdf file or a .docx file. By combining this pre-processing
@@ -47,7 +63,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
47
63
  version: '0'
48
64
  segments:
49
65
  - 0
50
- hash: 1250125497917414480
66
+ hash: 2506371109805164331
51
67
  required_rubygems_version: !ruby/object:Gem::Requirement
52
68
  none: false
53
69
  requirements:
@@ -56,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
56
72
  version: '0'
57
73
  segments:
58
74
  - 0
59
- hash: 1250125497917414480
75
+ hash: 2506371109805164331
60
76
  requirements: []
61
77
  rubyforge_project:
62
78
  rubygems_version: 1.8.25