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