liquidoc 0.12.0.pre.rc4 → 0.12.1.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/liquidoc.rb CHANGED
@@ -9,9 +9,11 @@ require 'logger'
9
9
  require 'csv'
10
10
  require 'crack/xml'
11
11
  require 'fileutils'
12
- require 'jekyll'
13
12
  require 'open3'
14
13
  require 'highline'
14
+ require 'liquid/tags/jekyll'
15
+ require 'liquid/filters/jekyll'
16
+ require 'sterile'
15
17
 
16
18
  # ===
17
19
  # Table of Contents
@@ -25,7 +27,8 @@ require 'highline'
25
27
  # 5a. parse procs def
26
28
  # 5b. migrate procs def
27
29
  # 5c. render procs def
28
- # 6. text manipulation modules/classes def
30
+ # 5d. execute procs def
31
+ # 6. text manipulation filters
29
32
  # 7. command/option parser def
30
33
  # 8. executive proc calls
31
34
 
@@ -39,13 +42,14 @@ require 'highline'
39
42
  @build_dir = @build_dir_def
40
43
  @configs_dir = @base_dir + '_configs'
41
44
  @templates_dir = @base_dir + '_templates/'
45
+ @includes_dirs_def = ['_templates','_templates/liquid','_templates/liquid/ops','_templates/ops','theme/_includes','theme/_layouts']
46
+ @includes_dirs = @includes_dirs_def
42
47
  @data_dir = @base_dir + '_data/'
43
48
  @data_files = nil
44
49
  @attributes_file_def = '_data/asciidoctor.yml'
45
50
  @attributes_file = @attributes_file_def
46
51
  @pdf_theme_file = 'theme/pdf-theme.yml'
47
52
  @fonts_dir = 'theme/fonts/'
48
- @output_filename = 'index'
49
53
  @attributes = {}
50
54
  @passed_attrs = {}
51
55
  @passed_vars = {}
@@ -80,7 +84,8 @@ FileUtils::mkdir_p("#{@build_dir}/pre") unless File.exists?("#{@build_dir}/pre")
80
84
  def config_build config_file, config_vars={}, data_files=nil, parse=false
81
85
  @logger.debug "Using config file #{config_file}."
82
86
  validate_file_input(config_file, "config")
83
- if config_vars.length > 0 or data_files or parse or contains_liquid(config_file)
87
+ config_base = File.read(config_file)
88
+ if config_vars.length > 0 or data_files or parse or config_base.contains_liquid?
84
89
  @logger.debug "Config_vars: #{config_vars.length}"
85
90
  # If config variables are passed on the CLI, we want to parse the config file
86
91
  # and use the parsed version for the rest fo this routine
@@ -94,8 +99,8 @@ def config_build config_file, config_vars={}, data_files=nil, parse=false
94
99
  liquify(data_obj, config_file, config_out)
95
100
  config_file = config_out
96
101
  @logger.debug "Config parsed! Using #{config_out} for build."
97
- validate_file_input(config_file, "config")
98
102
  end
103
+ validate_file_input(config_file, "config")
99
104
  begin
100
105
  config = YAML.load_file(config_file)
101
106
  rescue Exception => ex
@@ -106,6 +111,8 @@ def config_build config_file, config_vars={}, data_files=nil, parse=false
106
111
  end
107
112
  raise "ConfigFileError"
108
113
  end
114
+ # TESTS
115
+ # puts config[0].argify
109
116
  cfg = BuildConfig.new(config) # convert the config file to a new object called 'cfg'
110
117
  if @safemode
111
118
  commands = ""
@@ -146,6 +153,10 @@ def iterate_build cfg
146
153
  # Prep & perform a Liquid-parsed build
147
154
  @explainer.info build.message
148
155
  build.add_data!(build.variables, "vars") if build.variables
156
+ includes_dirs = @includes_dirs
157
+ includes_dirs = build.includes_dirs if build.includes_dirs
158
+ includes_dirs.unshift(File.dirname(build.template))
159
+ build.add_data!(includes_dirs: includes_dirs)
149
160
  liquify(build.data, build.template, build.output) # perform the liquify operation
150
161
  else # Prep & perform a direct conversion
151
162
  # Delete nested data and vars objects
@@ -153,7 +164,6 @@ def iterate_build cfg
153
164
  build.data.remove_scope("vars")
154
165
  # Add vars from CLI or config args
155
166
  build.data.add_data!(build.variables) unless build.variables.empty?
156
- build.data.add_data!(@passed_vars) unless @passed_vars.empty?
157
167
  regurgidata(build.data, build.output)
158
168
  end
159
169
  end
@@ -170,8 +180,10 @@ def iterate_build cfg
170
180
  builds = step.builds
171
181
  for bld in builds
172
182
  doc = AsciiDocument.new(step.source)
173
- attrs = ingest_attributes(step.data) if step.data # Set attributes from YAML files
174
- doc.add_attrs!(attrs) # Set attributes from the action-level data file
183
+ if step.data
184
+ attrs = ingest_attributes(step.data)
185
+ doc.add_attrs!(attrs) # Set attributes from the action-level data file
186
+ end
175
187
  build = Build.new(bld, type) # create an instance of the Build class; Build.new accepts a 'bld' hash & action 'type' string
176
188
  build.set("backend", derive_backend(doc.type, build.output) ) unless build.backend
177
189
  @explainer.info build.message
@@ -210,31 +222,6 @@ def validate_file_input file, type
210
222
  end
211
223
  end
212
224
 
213
- def validate_config_structure config
214
- unless config.is_a? Array
215
- message = "The configuration file is not properly structured."
216
- @logger.error message
217
- raise "ConfigStructError"
218
- else
219
- if (defined?(config['action'])).nil?
220
- message = "Every listing in the configuration file needs an action type declaration."
221
- @logger.error message
222
- raise "ConfigStructError"
223
- end
224
- end
225
- # TODO More validation needed
226
- end
227
-
228
- def contains_liquid filename
229
- File.open(filename, "r") do |file_proc|
230
- file_proc.each_line do |row|
231
- if row.match(/.*\{\%.*\%\}.*|.*\{\{.*\}\}.*/)
232
- return true
233
- end
234
- end
235
- end
236
- end
237
-
238
225
  def explainer_init out=nil
239
226
  unless @explainer
240
227
  if out == "STDOUT"
@@ -285,12 +272,7 @@ class BuildConfig
285
272
  if (defined?(config['compile'][0])) # The config is formatted for vesions < 0.3.0; convert it
286
273
  config = deprecated_format(config)
287
274
  end
288
-
289
- # validations
290
- unless config.is_a? Array
291
- raise "ConfigStructError"
292
- end
293
-
275
+ validate(config)
294
276
  @cfg = config
295
277
  end
296
278
 
@@ -307,6 +289,13 @@ class BuildConfig
307
289
  return config['compile']
308
290
  end
309
291
 
292
+ def validate config
293
+ unless config.is_a? Array
294
+ raise "ConfigStructError"
295
+ end
296
+ # TODO More validation needed
297
+ end
298
+
310
299
  end #class BuildConfig
311
300
 
312
301
  class BuildConfigStep
@@ -314,7 +303,7 @@ class BuildConfigStep
314
303
  def initialize step
315
304
  @step = step
316
305
  if (defined?(@step['action'])).nil?
317
- raise "ConfigStructError"
306
+ raise "StepStructError"
318
307
  end
319
308
  @step['options'] = nil unless defined?(step['options'])
320
309
  validate()
@@ -415,7 +404,7 @@ class BuildConfigStep
415
404
  for req in reqs
416
405
  if (defined?(@step[req])).nil?
417
406
  @logger.error "Every #{@step['action']}-type in the configuration file needs a '#{req}' declaration."
418
- raise "ConfigStructError"
407
+ raise "ConfigStepError"
419
408
  end
420
409
  end
421
410
  end
@@ -437,6 +426,10 @@ class Build
437
426
  @build['template']
438
427
  end
439
428
 
429
+ def includes_dirs
430
+ @build['includes_dirs']
431
+ end
432
+
440
433
  def output
441
434
  @build['output']
442
435
  end
@@ -481,7 +474,7 @@ class Build
481
474
  reason = ", #{@build['reason']}" if @build['reason']
482
475
  case @type
483
476
  when "parse"
484
- text = ".. Builds `#{self.output}` pressed with the template `#{self.template}`#{reason}."
477
+ text = ".. Builds `#{self.output}` parsed with the template `#{self.template}`#{reason}."
485
478
  when "render"
486
479
  case self.backend
487
480
  when "pdf"
@@ -646,14 +639,14 @@ class DataSrc
646
639
  datatype = "yml"
647
640
  end
648
641
  else # If there's no 'type' defined, extract it from the filename and validate it
649
- unless @datasrc['ext'].downcase.match(/\.yml|\.json|\.xml|\.csv/)
642
+ unless @datasrc['ext'].downcase.match(/\.yml|\.json|\.xml|\.csv|\.adoc/)
650
643
  # @logger.error "Data file extension must be one of: .yml, .json, .xml, or .csv or else declared in config file."
651
644
  raise "FileExtensionUnknown"
652
645
  end
653
646
  datatype = self.ext
654
647
  datatype = datatype[1..-1] # removes leading dot char
655
648
  end
656
- unless datatype.downcase.match(/yml|json|xml|csv|regex/) # 'type' must be one of these permitted vals
649
+ unless datatype.downcase.match(/yml|json|xml|csv|regex|adoc/) # 'type' must be one of these permitted vals
657
650
  # @logger.error "Declared data type must be one of: yaml, json, xml, csv, or regex."
658
651
  raise "DataTypeUnrecognized"
659
652
  end
@@ -834,6 +827,13 @@ def ingest_data datasrc
834
827
  @logger.error "You must supply a regex pattern with your free-form data file."
835
828
  raise "MissingRegexPattern"
836
829
  end
830
+ when "adoc"
831
+ begin
832
+ doc = Asciidoctor.load_file(datasrc.file)
833
+ data = doc.attributes
834
+ rescue
835
+ @logger.error "Problem with AsciiDoc source file. Attributes not ingested."
836
+ end
837
837
  end
838
838
  return data
839
839
  end
@@ -865,8 +865,10 @@ def parse_regex data_file, pattern
865
865
  end
866
866
 
867
867
  # Parse given data using given template, generating given output
868
- def liquify data_obj, template_file, output
868
+ def liquify data_obj, template_file, output="stdout"
869
869
  validate_file_input(template_file, "template")
870
+ # inject :includes_dirs as needed
871
+ data_obj.add_data!({'includes_dirs' => @includes_dirs}) unless data_obj.data['includes_dirs']
870
872
  begin
871
873
  template = File.read(template_file) # reads the template file
872
874
  template = Liquid::Template.parse(template) # compiles template
@@ -877,17 +879,22 @@ def liquify data_obj, template_file, output
877
879
  @logger.error message
878
880
  raise message
879
881
  end
880
- unless output.downcase == "stdout"
882
+ unless output == "stdout" || @output_type == "stdout"
881
883
  output_file = output
882
884
  generate_file(rendered, output_file)
883
885
  else # if stdout
884
- puts "========\nOUTPUT: Rendered with template #{template_file}:\n\n#{rendered}\n"
886
+ puts rendered
885
887
  end
886
888
  end
887
889
 
888
890
  def cli_liquify data_files=nil, template_file=nil, output_file=nil, passed_vars
889
891
  # converts command-line options into liquify or regurgidata inputs
890
892
  data_obj = DataObj.new()
893
+ if output_file
894
+ output = output_file
895
+ else
896
+ output = "stdout"
897
+ end
891
898
  if data_files
892
899
  payload = get_payload(data_files)
893
900
  data_obj.add_payload!(payload)
@@ -895,16 +902,16 @@ def cli_liquify data_files=nil, template_file=nil, output_file=nil, passed_vars
895
902
  if template_file
896
903
  # data_obj.add_data!(ingested, "data") if df
897
904
  data_obj.add_data!(passed_vars, "vars") if passed_vars
898
- liquify(data_obj, template_file, output_file)
905
+ liquify(data_obj, template_file, output)
899
906
  else
900
907
  data_obj.remove_scope("vars")
901
908
  data_obj.add_data!(passed_vars) if passed_vars
902
- regurgidata(data_obj, output_file)
909
+ regurgidata(data_obj, output)
903
910
  end
904
911
  end
905
912
 
906
913
  def regurgidata data_obj, output
907
- # converts data files from one format directly to another
914
+ # converts data object from one format directly to another
908
915
  raise "UnrecognizedFileExtension" unless File.extname(output).match(/\.yml|\.json|\.xml|\.csv/)
909
916
  case File.extname(output)
910
917
  when ".yml"
@@ -919,7 +926,6 @@ def regurgidata data_obj, output
919
926
  if new_data
920
927
  begin
921
928
  generate_file(new_data, output)
922
- # File.open(output, 'w') { |file| file.write(new_data) }
923
929
  @logger.info "Data converted and saved to #{output}."
924
930
  rescue Exception => ex
925
931
  @logger.error "#{ex.class}: #{ex.message}"
@@ -1140,7 +1146,7 @@ def jekyll_serve build
1140
1146
  @logger.debug "Attempting Jekyll serve operation."
1141
1147
  config_file = build.props['files'][0]
1142
1148
  if build.props['arguments']
1143
- opts_args = build.props['arguments'].to_opts_args
1149
+ opts_args = build.props['arguments'].argify
1144
1150
  end
1145
1151
  command = "bundle exec jekyll serve --config #{config_file} #{opts_args} --no-watch --skip-initial-build"
1146
1152
  system command
@@ -1182,7 +1188,7 @@ def execute_command cmd
1182
1188
  contents = stdout
1183
1189
  if cmd.options['outfile']
1184
1190
  contents = "#{cmd.options['outfile']['prepend']}\n#{stdout}" if cmd.options['outfile']['prepend']
1185
- contents = "#{stdout}/n#{cmd.options['outfile']['append']}" if cmd.options['outfile']['append']
1191
+ contents = "#{stdout}\n#{cmd.options['outfile']['append']}" if cmd.options['outfile']['append']
1186
1192
  generate_file(contents, cmd.options['outfile']['path'])
1187
1193
  end
1188
1194
  if cmd.options['stdout']
@@ -1196,24 +1202,6 @@ end
1196
1202
  # Text manipulation Classes, Modules, procs, etc
1197
1203
  # ===
1198
1204
 
1199
- module HashMash
1200
-
1201
- def to_opts_args
1202
- out = ''
1203
- if self.is_a? Hash # TODO Should also be testing for flatness
1204
- self.each do |opt,arg|
1205
- out = out + " --#{opt} #{arg}"
1206
- end
1207
- end
1208
- return out
1209
- end
1210
-
1211
- end
1212
-
1213
- class Hash
1214
- include HashMash
1215
- end
1216
-
1217
1205
  module ForceArray
1218
1206
  # So we can accept a list string ("item1.yml,item2.yml") or a single item ("item1.yml")
1219
1207
  # and convert to array as needed
@@ -1227,77 +1215,375 @@ module ForceArray
1227
1215
  obj = Array.new.push(obj)
1228
1216
  end
1229
1217
  else
1230
- raise "ForceArrayFail"
1218
+ if obj.class == Hash
1219
+ obj = obj.to_array
1220
+ else
1221
+ raise "ForceArrayFail"
1222
+ end
1231
1223
  end
1232
1224
  end
1233
1225
  return obj.to_ary
1234
1226
  end
1235
1227
 
1228
+ def force_array!
1229
+ self.force_array
1230
+ end
1231
+
1236
1232
  end
1237
1233
 
1238
1234
  class String
1239
1235
  include ForceArray
1240
- # Adapted from Nikhil Gupta
1241
- # http://nikhgupta.com/code/wrapping-long-lines-in-ruby-for-display-in-source-files/
1236
+ # Adapted from Nikhil Gupta
1237
+ # http://nikhgupta.com/code/wrapping-long-lines-in-ruby-for-display-in-source-files/
1242
1238
  def wrap options = {}
1243
- width = options.fetch(:width, 76)
1244
- commentchar = options.fetch(:commentchar, '')
1239
+ width = options.fetch(:width, 76) # length to wrap at
1240
+ pre = options.fetch(:prepend, '') # text to prepend
1241
+ app = options.fetch(:append, '') # text to append
1242
+ chars = pre.size + app.size
1245
1243
  self.strip.split("\n").collect do |line|
1246
- line.length > width ? line.gsub(/(.{1,#{width}})(\s+|$)/, "\\1\n#{commentchar}") : line
1247
- end.map(&:strip).join("\n#{commentchar}")
1244
+ line.length + chars.size > width ? line.gsub(/(.{1,#{(width - chars)}})(\s+|$)/, "#{pre}\\1#{app}\n") : "#{pre}#{line}#{app}\n"
1245
+ end.map(&:rstrip).join("\n")
1248
1246
  end
1249
1247
 
1250
1248
  def indent options = {}
1251
- spaces = " " * options.fetch(:spaces, 4)
1252
- self.gsub(/^/, spaces).gsub(/^\s*$/, '')
1249
+ # TODO: does not allow tabs; inserts explicit `\t` string
1250
+ syms = options.fetch(:sym, ' ') * options.fetch(:by, 2)
1251
+ self.gsub!(/^/m, "#{syms}")
1252
+ self.sub!("#{syms}", "") unless options.fetch(:line1, false)
1253
+ end
1254
+
1255
+ def contains_liquid?
1256
+ self.each_line do |row|
1257
+ if row.match(/.*\{\%.*\%\}.*|.*\{\{.*\}\}.*/)
1258
+ return true
1259
+ end
1260
+ end
1261
+ return false
1253
1262
  end
1254
1263
 
1255
- def indent_with_wrap options = {}
1256
- spaces = options.fetch(:spaces, 4)
1257
- width = options.fetch(:width, 80)
1258
- width = width > spaces ? width - spaces : 1
1259
- self.wrap(width: width).indent(spaces: spaces)
1264
+ def quote_wrap options = {}
1265
+ # When a string contains a certain pattern, wrap it in certain quotes
1266
+ # Pass '\s' as pattern to wrap any string that contains 1 or more spaces or tabs
1267
+ # pass '.' as pattern to always wrap.
1268
+
1269
+ pattern = options.fetch(:pattern, '\s').to_s
1270
+ return self unless self.strip.match(/\s/)
1271
+ quotes = options.fetch(:quotes, "single")
1272
+ case quotes
1273
+ when "single"
1274
+ wrap = "''"
1275
+ when "double"
1276
+ wrap = '""'
1277
+ when "backtick"
1278
+ wrap = "``"
1279
+ when "bracket"
1280
+ wrap = "[]"
1281
+ else
1282
+ wrap = quotes
1283
+ end
1284
+ quotes << wrap[0] unless wrap[1]
1285
+ return wrap[0] + self.strip + wrap[1]
1260
1286
  end
1261
1287
 
1262
1288
  end
1263
1289
 
1264
1290
  class Array
1265
1291
  include ForceArray
1292
+
1293
+ def to_hash
1294
+ struct = {}
1295
+ self.each do |p|
1296
+ struct.merge!p if p.is_a? Hash
1297
+ end
1298
+ return struct
1299
+ end
1300
+
1301
+ # Get all unique values for each item in an array, or each unique value of a desigated
1302
+ # parameter in an array of hashes.
1303
+ #
1304
+ # @input : the object array
1305
+ # @property : (optional) parameter in which to select unique values (for hashes)
1306
+ def unique_property_values property=nil
1307
+ return self.uniq unless property
1308
+ new_ary = self.uniq { |i| i[property] }
1309
+ out = new_ary.map { |i| i[property] }.compact
1310
+ out
1311
+ end
1312
+
1313
+ def concatenate_property_instances property=String
1314
+ # flattens the values of instances of a given property throughout an array of Hashes
1315
+ all_arrays = []
1316
+ self.each do |i|
1317
+ all_arrays << i[property]
1318
+ end
1319
+ return all_arrays.flatten
1320
+ end
1321
+
1322
+ def repeated_property_values property=String
1323
+ # testing for uniqueness globally among all values in subarrays (list-formatted values) of all instances of the property across all nodes in the parent array
1324
+ # returns an array of duplicate items among all the tested arrays
1325
+ #
1326
+ # Example:
1327
+ # array_of_hashes[0]['cue'] = ['one','two','three']
1328
+ # array_of_hashes[1]['cue'] = ['three','four','five']
1329
+ # array_of_hashes.duplicate_property_values('cue')
1330
+ # #=> ['three']
1331
+ # Due to the apperance of 'three' in both instances of cue.
1332
+ firsts = []
1333
+ dupes = []
1334
+ self.each do |node|
1335
+ return ['non-array property value present'] unless node[property].is_a? Array
1336
+ node[property].each do |i|
1337
+ dupes << i if firsts.include? i
1338
+ firsts << i
1339
+ end
1340
+ end
1341
+ return dupes
1342
+ end
1343
+
1344
+ end
1345
+
1346
+ class Hash
1347
+ include ForceArray
1348
+
1349
+ def to_array op=nil
1350
+ # Converts a hash of key-value pairs to a flat array based on the first tier
1351
+ out = []
1352
+ self.each do |k,v|
1353
+ v = "<RemovedObject>" if v.is_a? Enumerable and op == "flatten"
1354
+ out << {k => v}
1355
+ end
1356
+ return out
1357
+ end
1358
+
1359
+ def argify options = {}
1360
+ # Converts a hash of key-value pairs to command-line option/argument listings
1361
+ # Can be called with optional arguments:
1362
+ # template :: Liquid-formatted parsing template string
1363
+ # Accepts:
1364
+ #
1365
+ # 'hyph' :: -<key> <value>
1366
+ # 'hyphhyph' :: --<key> <value> (default)
1367
+ # 'hyphchar' :: -<k> <value>
1368
+ # 'dump' :: <key> <value>
1369
+ # 'paramequal' :: <key>=<value>
1370
+ # 'valonly' :: <value>
1371
+ # delim :: Delimiter -- any ASCII characters that separate the arguments
1372
+ #
1373
+ # For template-based usage, express the variables:
1374
+ # opt (the keyname) as {{opt}}
1375
+ # arg (the value) as {{arg}}
1376
+ # EXAMPLES (my_hash = {"key1"=>"val1", "key2"=>"val2"})
1377
+ # my_hash.argify #=> key1 val1 key2 val2
1378
+ # my_hash.argify('hyphhyph') #=> --key1 val1 --key2 val2
1379
+ # my_hash.argify('paramequal') #=> key1=val1 key2=val2
1380
+ # my_hash.argify('-a {{opt}}={{arg}}')#=> -a key1=val1 -a key2=val2
1381
+ # my_hash.argify('valonly', '||') #=> val1||val2
1382
+ # my_hash.argify("{{opt}} `{{arg}}`") #=> key1 `val1` key2 `val2`
1383
+ raise "InvalidObject" unless self.is_a? Hash
1384
+ template = options.fetch(:template, 'hyphhyph')
1385
+ if template.contains_liquid?
1386
+ tp = template # use the passed Liquid template
1387
+ else
1388
+ case template # use a preset Liquid template by name
1389
+ when "dump"
1390
+ tp = "{{opt}} {{arg | quote_wrap: 'single', '\s|,' }}"
1391
+ when "hyph"
1392
+ tp = "-{{opt}} {{arg | quote_wrap: 'single', '\s|,' }}"
1393
+ when "hyphhyph"
1394
+ tp = "--{{opt}} {{arg | quote_wrap: 'single', '\s|,' }}"
1395
+ when "paramequal"
1396
+ tp = "{{opt}}={{arg | quote_wrap: 'single', '\s|,' }}"
1397
+ when "valonly"
1398
+ tp = "{{arg | quote_wrap: 'single', '\s|,' }}"
1399
+ else
1400
+ return "Liquid: Unrecognized argify template name: #{template}"
1401
+ end
1402
+ end
1403
+ begin
1404
+ tpl = Liquid::Template.parse(tp)
1405
+ first = true
1406
+ out = ''
1407
+ self.each do |k,v|
1408
+ # establish datasource
1409
+ v = "<Object>" if v.is_a? Hash
1410
+ v = v.join(',') if v.is_a? Array
1411
+ input = {"opt" => k.to_s, "arg" => v.to_s }
1412
+ if first
1413
+ dlm = ""
1414
+ first = false
1415
+ else
1416
+ dlm = options.fetch(:delim, ' ')
1417
+ end
1418
+ out += dlm + tpl.render(input)
1419
+ end
1420
+ rescue
1421
+ raise "Argify template processing failed"
1422
+ end
1423
+ return out
1424
+ end
1425
+
1426
+
1266
1427
  end
1267
1428
 
1268
1429
  # Extending Liquid filters/text manipulation
1269
- module CustomFilters
1430
+ module LiquiDocFilters
1270
1431
  include Jekyll::Filters
1432
+ #
1433
+ # sterile-based filters
1434
+ #
1435
+
1436
+ def to_slug input, delim='-'
1437
+ o = input.dup
1438
+ opts = {:delimiter=>delim}
1439
+ o.to_slug(opts)
1440
+ end
1441
+
1442
+ def transliterate input
1443
+ o = input.dup
1444
+ o.transliterate
1445
+ end
1446
+
1447
+ def smart_format input
1448
+ o = input.dup
1449
+ o.smart_format
1450
+ end
1451
+
1452
+ def encode_entities input
1453
+ o = input.dup
1454
+ o.encode_entities
1455
+ end
1271
1456
 
1272
- def plainwrap input
1273
- input.wrap
1457
+ def titlecase input
1458
+ o = input.dup
1459
+ o.titlecase
1274
1460
  end
1275
- def commentwrap input
1276
- input.wrap commentchar: "# "
1461
+
1462
+ def strip_tags input
1463
+ o = input.dup
1464
+ o.strip_tags
1465
+ end
1466
+
1467
+ def sterilize input
1468
+ o = input.dup
1469
+ o.sterilize
1277
1470
  end
1278
- def unwrap input # Not fully functional; inserts explicit '\n'
1471
+
1472
+ #
1473
+ # Custom Filters
1474
+ #
1475
+
1476
+ def where_uniq input, property, value
1477
+ o = input.where(input, property, value)
1478
+ o[0] if o.size == 1
1479
+ "No result" unless o.size
1480
+ "Multiple results" if o.size > 1
1481
+ end
1482
+
1483
+ def wrap input, width=80, prepend='', append='', vent=false
1484
+ input.wrap(:width => width, :prepend => prepend, :append => append)
1485
+ end
1486
+
1487
+ def plainwrap input, width=80
1488
+ input.wrap(:width => width)
1489
+ end
1490
+
1491
+ def commentwrap input, width=80, prepend='# '
1492
+ input.wrap(:width => width, :pre => prepend)
1493
+ end
1494
+
1495
+ def unwrap input, token1='&g59h%j1k;', token2='&ru8sf%df;'
1279
1496
  if input
1280
- token = "[g59hj1k]"
1281
- input.gsub(/\n\n/, token).gsub(/\n/, ' ').gsub(token, "\n\n")
1497
+ input.gsub(/(.)\n\n/, "\\1#{token1}").gsub(/([\."'])$\n([A-Z\(\_"'])/,"\\1#{token2}\\2").gsub(/\n/, '').gsub(token2,"\n").gsub(token1, "\n\n")
1282
1498
  end
1283
1499
  end
1284
1500
 
1285
- def slugify input
1286
- # Downcase
1287
- # Turn unwanted chars into the seperator
1501
+ def indent_lines input, by=2, sym=' ', line1=false
1502
+ input.indent(:by => by, :sym => "#{sym}", :line1 => line1)
1503
+ end
1504
+
1505
+ def slugify input, delim='-', snip=false
1288
1506
  s = input.to_s.downcase
1289
- s.gsub!(/[^a-zA-Z0-9\-_\+\/]+/i, "-")
1507
+ s.gsub!(/[^a-z0-9]/, delim)
1508
+ if snip
1509
+ while s.match("#{delim}#{delim}")
1510
+ s.gsub!("#{delim}#{delim}", "#{delim}")
1511
+ end
1512
+ s.gsub!(/^#{delim}+(.*)$/, "\\1")
1513
+ s.gsub!(/^(.*)#{delim}+$/, "\\1")
1514
+ end
1290
1515
  s
1291
1516
  end
1292
1517
 
1518
+ def asciidocify input
1519
+ Asciidoctor.convert(input, doctype: "inline")
1520
+ end
1521
+
1522
+ def quote_wrap input, quotes="''", pattern="\s"
1523
+ input.quote_wrap(:quotes => quotes, :pattern => pattern)
1524
+ end
1525
+
1526
+ def to_cli_args input, template="paramequal", delim=" "
1527
+ input.argify(:template => template, :delim => delim)
1528
+ end
1529
+
1530
+ def hash_to_array input, op=nil
1531
+ o = input.dup
1532
+ o.to_array(op)
1533
+ end
1534
+
1535
+ def holds_liquid input
1536
+ o = false
1537
+ o = true if input.contains_liquid?
1538
+ o
1539
+ end
1540
+
1541
+ def store_list_uniq input, property=nil
1542
+ input.unique_property_values(property)
1543
+ end
1544
+
1545
+ def store_list_concat input, property=String
1546
+ input.concatenate_property_instances(property)
1547
+ end
1548
+
1549
+ def store_list_dupes input, property=String
1550
+ input.repeated_property_values(property)
1551
+ end
1552
+
1293
1553
  def regexreplace input, regex, replacement=''
1554
+ # deprecated in favor of re_replace as of 0.12.0
1294
1555
  input.to_s.gsub(Regexp.new(regex), replacement.to_s)
1295
1556
  end
1296
1557
 
1558
+ def replace_regex input, regex, replacement='', multiline=true, global=true
1559
+ pattern = Regexp.new(regex, Regexp::MULTILINE) if multiline
1560
+ pattern = Regexp.new(regex) unless multiline
1561
+ o = input.to_s.gsub(pattern, replacement.to_s) if global
1562
+ o = input.to_s.sub(pattern, replacement.to_s) unless global
1563
+ o
1564
+ end
1565
+
1566
+ def match input, regex, multiline=true, global=true
1567
+ pattern = Regexp.new(regex, Regexp::MULTILINE) if multiline
1568
+ pattern = Regexp.new(regex) unless multiline
1569
+ return true if input.to_s.match(pattern)
1570
+ return false
1571
+ end
1572
+
1573
+ def to_yaml input
1574
+ o = input.to_yaml
1575
+ o = o.gsub(/^\-\-\-$\n/, "")
1576
+ o
1577
+ end
1578
+
1579
+ def to_json input
1580
+ o = input.to_json
1581
+ o
1582
+ end
1297
1583
  end
1298
1584
 
1299
- # register custom Liquid filters
1300
- Liquid::Template.register_filter(CustomFilters)
1585
+ # Register custom Liquid filters
1586
+ Liquid::Template.register_filter(LiquiDocFilters)
1301
1587
 
1302
1588
  # ===
1303
1589
  # Command/options parser
@@ -1317,15 +1603,15 @@ command_parser = OptionParser.new do|opts|
1317
1603
  end
1318
1604
 
1319
1605
  # Global Options
1320
- opts.on("-b PATH", "--base=PATH", "The base directory, relative to this script. Defaults to `.`, or pwd." ) do |n|
1606
+ opts.on("-b PATH", "--base PATH", "The base directory, relative to this script. Defaults to `.`, or pwd." ) do |n|
1321
1607
  @base_dir = n
1322
1608
  end
1323
1609
 
1324
- opts.on("-B PATH", "--build=PATH", "The directory under which LiquiDoc should save automatically preprocessed files. Defaults to #{@base_dir}_build. Can be absolute or relative to the base path (-b/--base=). Do NOT append '/' to the build path." ) do |n|
1610
+ opts.on("-B PATH", "--build PATH", "The directory under which LiquiDoc should save automatically preprocessed files. Defaults to #{@base_dir}_build. Can be absolute or relative to the base path (-b/--base=). Do NOT append '/' to the build path." ) do |n|
1325
1611
  @build_dir = n
1326
1612
  end
1327
1613
 
1328
- opts.on("-c", "--config=PATH", "Configuration file, enables preset source, template, and output.") do |n|
1614
+ opts.on("-c", "--config PATH", "Configuration file, enables preset source, template, and output.") do |n|
1329
1615
  @config_file = @base_dir + n
1330
1616
  end
1331
1617
 
@@ -1335,22 +1621,28 @@ command_parser = OptionParser.new do|opts|
1335
1621
  @data_files = DataFiles.new(data_files)
1336
1622
  end
1337
1623
 
1338
- opts.on("-f PATH", "--from=PATH", "Directory to copy assets from." ) do |n|
1624
+ opts.on("-f PATH", "--from PATH", "Directory to copy assets from." ) do |n|
1339
1625
  @attributes_file = n
1340
1626
  end
1341
1627
 
1342
- opts.on("-i PATH", "--index=PATH", "An AsciiDoc index file for mapping an Asciidoctor build." ) do |n|
1628
+ opts.on("-i PATH", "--index PATH", "An AsciiDoc index file for mapping an Asciidoctor build." ) do |n|
1343
1629
  @index_file = n
1344
1630
  end
1345
1631
 
1346
1632
  opts.on("-o PATH", "--output=PATH", "Output file path for generated content. Ex. path/to/file.adoc. Required unless --config is called.") do |n|
1347
- @output_file = @base_dir + n
1633
+ @output = @base_dir + n
1348
1634
  end
1349
1635
 
1350
- opts.on("-t PATH", "--template=PATH", "Path to liquid template. Required unless --configuration is called." ) do |n|
1636
+ opts.on("-t PATH", "--template PATH", "Path to liquid template. Required unless --configuration is called." ) do |n|
1351
1637
  @template_file = @base_dir + n
1352
1638
  end
1353
1639
 
1640
+ opts.on("--includes_dirs PATH[,PATH]", "Paths to directories where includes (partials) can be found. Overrides default directories." ) do |n|
1641
+ n = n.force_array
1642
+ # n.map { |p| @base_dir + p }
1643
+ @includes_dirs = @includes_dirs.concat n
1644
+ end
1645
+
1354
1646
  opts.on("--verbose", "Run verbose debug logging.") do |n|
1355
1647
  @logger.level = Logger::DEBUG
1356
1648
  @verbose = true
@@ -1369,7 +1661,7 @@ command_parser = OptionParser.new do|opts|
1369
1661
  end
1370
1662
 
1371
1663
  opts.on("--stdout", "Puts the output in STDOUT instead of writing to a file.") do
1372
- @output_type = "stdout"
1664
+ @output = "stdout"
1373
1665
  end
1374
1666
 
1375
1667
  opts.on("--deploy", "EXPERIMENTAL: Trigger a jekyll serve operation against the destination dir of a Jekyll render step.") do
@@ -1425,7 +1717,7 @@ explainer_init
1425
1717
  unless @config_file
1426
1718
  @logger.debug "Executing config-free build based on API/CLI arguments alone."
1427
1719
  if @data_files
1428
- cli_liquify(@data_files, @template_file, @output_file, @passed_vars)
1720
+ cli_liquify(@data_files, @template_file, @output, @passed_vars)
1429
1721
  end
1430
1722
  if @index_file
1431
1723
  @logger.warn "Rendering via command line arguments is not yet implemented. Use a config file."