liquidoc 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6066fed122738d3eb82ad92365208357bba0fc97
4
- data.tar.gz: 55acbe02d698fea8c72ebaae61daa8f3a23584cc
3
+ metadata.gz: 680eeb03f5bdca5f09d010b37c2ba8a4fa2f7060
4
+ data.tar.gz: c5b06675ef4d7e2fcdcf5e0e1838295864edf094
5
5
  SHA512:
6
- metadata.gz: 460472382d8369294a1857b16e1444610d540d5805ba95c7ceb72faa46962305720f45fdf7a9d5d65c724cfe541154a0cbf46853ca0a17d914dca62c9314ffac
7
- data.tar.gz: d234c41558f62429e2d23f06bafc01bc9db7302933d9ca5e09f58b0d8679a0a5caf90c80a98564ddc053b47b3aae7ced2d1dd062887d7426747a508906759284
6
+ metadata.gz: 9658b656c88006e9fae3d16e35c00f12132f19a57fc963241650bc5a6b2849ce7efa4198f101c51f319f0b674936d624c29cd67ae5d292082cda773bebe5e77b
7
+ data.tar.gz: d63050fcd42fce26362c7cf9300a75f2a51213da2606e3cfb2fed84792b249c7b9a317da21120dc91194657124e11fdb1e9dc0a2378f0aa41f2046b2382d0f8c
@@ -1,3 +1,3 @@
1
1
  module Liquidoc
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0"
3
3
  end
data/lib/liquidoc.rb CHANGED
@@ -9,6 +9,7 @@ require 'logger'
9
9
  require 'csv'
10
10
  require 'crack/xml'
11
11
  require 'fileutils'
12
+ require 'jekyll'
12
13
 
13
14
  # ===
14
15
  # Table of Contents
@@ -32,6 +33,8 @@ require 'fileutils'
32
33
 
33
34
  @base_dir_def = Dir.pwd + '/'
34
35
  @base_dir = @base_dir_def
36
+ @build_dir_def = @base_dir + '_build'
37
+ @build_dir = @build_dir_def
35
38
  @configs_dir = @base_dir + '_configs'
36
39
  @templates_dir = @base_dir + '_templates/'
37
40
  @data_dir = @base_dir + '_data/'
@@ -43,12 +46,20 @@ require 'fileutils'
43
46
  @attributes = {}
44
47
  @passed_attrs = {}
45
48
  @verbose = false
49
+ @quiet = false
50
+ @explicit = false
46
51
 
52
+ # Instantiate the main Logger object, which is always running
47
53
  @logger = Logger.new(STDOUT)
48
- @logger.level = Logger::INFO
49
54
  @logger.formatter = proc do |severity, datetime, progname, msg|
50
55
  "#{severity}: #{msg}\n"
51
56
  end
57
+ @logger.level = Logger::INFO # suppresses DEBUG-level messages
58
+
59
+
60
+ FileUtils::mkdir_p("#{@build_dir}") unless File.exists?("#{@build_dir}")
61
+ FileUtils::mkdir_p("#{@build_dir}/pre") unless File.exists?("#{@build_dir}/pre")
62
+
52
63
 
53
64
  # ===
54
65
  # Executive procs
@@ -77,6 +88,7 @@ def iterate_build cfg
77
88
  for step in cfg.steps # iterate through each node in the 'config' object, which should start with an 'action' parameter
78
89
  stepcount = stepcount + 1
79
90
  step = BuildConfigStep.new(step) # create an instance of the Action class, validating the top-level step hash (now called 'step') in the process
91
+ @explainer.info step.message
80
92
  type = step.type
81
93
  case type # a switch to evaluate the 'action' parameter for each step in the iteration...
82
94
  when "parse"
@@ -85,7 +97,8 @@ def iterate_build cfg
85
97
  for bld in builds
86
98
  build = Build.new(bld, type) # create an instance of the Build class; Build.new accepts a 'bld' hash & action 'type'
87
99
  if build.template
88
- liquify(data, build.template, build.output) # perform the liquify operation
100
+ @explainer.info build.message
101
+ liquify(data, build.template, build.output, build.variables) # perform the liquify operation
89
102
  else
90
103
  regurgidata(data, build.output)
91
104
  end
@@ -96,12 +109,14 @@ def iterate_build cfg
96
109
  copy_assets(step.source, step.target, inclusive)
97
110
  when "render"
98
111
  validate_file_input(step.source, "source") if step.source
99
- doc = AsciiDocument.new(step.source)
100
- attrs = ingest_attributes(step.data) if step.data # Set attributes in from YAML files
101
- doc.add_attrs!(attrs) # Set attributes from the action-level data file
102
112
  builds = step.builds
103
113
  for bld in builds
114
+ doc = AsciiDocument.new(step.source)
115
+ attrs = ingest_attributes(step.data) if step.data # Set attributes from from YAML files
116
+ doc.add_attrs!(attrs) # Set attributes from the action-level data file
104
117
  build = Build.new(bld, type) # create an instance of the Build class; Build.new accepts a 'bld' hash & action 'type' string
118
+ build.set("backend", derive_backend(doc.type, build.output) ) unless build.backend
119
+ @explainer.info build.message
105
120
  render_doc(doc, build) # perform the render operation
106
121
  end
107
122
  when "deploy"
@@ -145,6 +160,27 @@ def validate_config_structure config
145
160
  # TODO More validation needed
146
161
  end
147
162
 
163
+ def explainer_init out=nil
164
+ unless @explainer
165
+ if out == "STDOUT"
166
+ @explainer = Logger.new(STDOUT)
167
+ else
168
+ out = "#{@build_dir}/pre/config-explainer.adoc" if out.nil?
169
+ File.open(out, 'w') unless File.exists?(out)
170
+ file = File.open(out, File::WRONLY)
171
+ begin
172
+ @explainer = Logger.new(file)
173
+ rescue Exception => ex
174
+ @logger.error ex
175
+ raise "ExplainerCreateError"
176
+ end
177
+ end
178
+ @explainer.formatter = proc do |severity, datetime, progname, msg|
179
+ "#{msg}\n"
180
+ end
181
+ end
182
+ end
183
+
148
184
  # ===
149
185
  # Core classes
150
186
  # ===
@@ -211,10 +247,59 @@ class BuildConfigStep
211
247
  return @step['options']
212
248
  end
213
249
 
250
+ def stage
251
+ return @step['stage']
252
+ end
253
+
214
254
  def builds
215
255
  return @step['builds']
216
256
  end
217
257
 
258
+ def message
259
+ # dynamically build a human-friendly log message, possibly appending a reason
260
+ unless @step['message']
261
+ reason = ", #{@step['reason']}" if @step['reason']
262
+ noninclusively = ", without carrying the parent directory" if self.options.is_a?(Hash) && self.options['inclusive'] == false && File.directory?(self.source)
263
+ stage = "" ; stage = "[#{self.stage}] " if self.stage
264
+ case self.type
265
+ when "migrate"
266
+ text = ". #{stage}Copies `#{self.source}` to `#{self.target}`#{noninclusively}#{reason}."
267
+ when "parse"
268
+ if self.data.is_a? Array
269
+ if self.data.count > 1
270
+ text = ". Draws data from the following files:"
271
+ self.data.each do |file|
272
+ text.concat("\n * `#{file}`.")
273
+ end
274
+ text.concat("\n")
275
+ else
276
+ text = ". #{stage}Draws data from `#{self.data[0]}`"
277
+ end
278
+ else
279
+ text = ". #{stage}Draws data from `#{self.data['file']}`"
280
+ end
281
+ text.concat("#{reason},") if reason
282
+ text.concat(" and parses it as follows:")
283
+ return text
284
+ when "render"
285
+ if self.source
286
+ text = ". #{stage}Using the index file `#{self.source}` as a map#{reason}, and ingesting AsciiDoc attributes from "
287
+ if self.data.is_a? Array
288
+ text.concat("the following data files:")
289
+ self.data.each do |file|
290
+ text.concat("\n * `#{file}`.")
291
+ end
292
+ else
293
+ text.concat("`#{self.data}`")
294
+ end
295
+ return text
296
+ end
297
+ end
298
+ else
299
+ return @step['message']
300
+ end
301
+ end
302
+
218
303
  def validate
219
304
  case self.type
220
305
  when "parse"
@@ -267,6 +352,58 @@ class Build
267
352
  @build['props']
268
353
  end
269
354
 
355
+ def variables
356
+ @build['variables']
357
+ end
358
+
359
+ def message
360
+ # dynamically build a message, possibly appending a reason
361
+ unless @build['message']
362
+ reason = ", #{@build['reason']}" if @build['reason']
363
+ case @type
364
+ when "parse"
365
+ text = ".. Builds `#{self.output}` pressed with the template `#{self.template}`#{reason}."
366
+ when "render"
367
+ case self.backend
368
+ when "pdf"
369
+ text = ".. Uses Asciidoctor/Prawn to generate a PDF file `#{self.output}`"
370
+ text.concat("#{reason}") if reason
371
+ text.concat(".")
372
+ when "html5"
373
+ text = ".. Compiles a standard Asciidoctor HTML5 file, `#{self.output}`"
374
+ text.concat("#{reason}") if reason
375
+ text.concat(".")
376
+ when "jekyll"
377
+ text = ".. Uses Jekyll config files:\n+\n--"
378
+ files = self.props['files']
379
+ if files.is_a? String
380
+ if files.include? ","
381
+ files = files.split(",")
382
+ else
383
+ files = files.split
384
+ end
385
+ else
386
+ unless files.is_a? Array
387
+ @logger.error "The Jekyll configuration file must be a single filename, a comma-separated list of filenames, or an array of filenames."
388
+ end
389
+ end
390
+ files.each do |file|
391
+ text.concat("\n * `#{file}`")
392
+ end
393
+ text.concat("\n\nto generate a static site")
394
+ if self.props && self.props['arguments']
395
+ text.concat(" at `#{self.props['arguments']['destination']}`")
396
+ end
397
+ text.concat("#{reason}") if reason
398
+ text.concat(".\n--\n")
399
+ end
400
+ return text
401
+ end
402
+ else
403
+ @build['message']
404
+ end
405
+ end
406
+
270
407
  def prop_files_array
271
408
  if props
272
409
  if props['files']
@@ -333,7 +470,7 @@ class Build
333
470
  end
334
471
  end
335
472
 
336
- end #class Build
473
+ end # class Build
337
474
 
338
475
  class DataSrc
339
476
  # initialization means establishing a proper hash for the 'data' param
@@ -355,8 +492,12 @@ class DataSrc
355
492
  else
356
493
  if datasrc.is_a? String
357
494
  @datasrc['ext'] = File.extname(datasrc)
358
- else # datasrc is neither string nor hash
359
- raise "InvalidDataSource"
495
+ else
496
+ if datasrc.is_a? Array
497
+
498
+ else
499
+ raise "InvalidDataSource"
500
+ end
360
501
  end
361
502
  end
362
503
  end
@@ -522,9 +663,13 @@ def parse_regex data_file, pattern
522
663
  end
523
664
 
524
665
  # Parse given data using given template, generating given output
525
- def liquify datasrc, template_file, output
666
+ def liquify datasrc, template_file, output, variables=nil
526
667
  data = get_data(datasrc)
527
668
  validate_file_input(template_file, "template")
669
+ if variables
670
+ vars = { "vars" => variables }
671
+ data.merge!vars
672
+ end
528
673
  begin
529
674
  template = File.read(template_file) # reads the template file
530
675
  template = Liquid::Template.parse(template) # compiles template
@@ -546,7 +691,7 @@ def liquify datasrc, template_file, output
546
691
  raise "FileNotBuilt"
547
692
  end
548
693
  if File.exists?(output_file)
549
- @logger.info "File built: #{File.basename(output_file)}"
694
+ @logger.info "File built: #{output_file}"
550
695
  else
551
696
  @logger.error "Hrmp! File not built."
552
697
  raise "FileNotBuilt"
@@ -638,7 +783,11 @@ def ingest_attributes attr_file
638
783
  raise "AttributeBlockError"
639
784
  end
640
785
  begin
641
- attrs.merge!new_attrs
786
+ if new_attrs.is_a? Hash
787
+ attrs.merge!new_attrs
788
+ else
789
+ @logger.warn "The AsciiDoc attributes file #{filename} is not formatted as a hash, so its data was not ingested."
790
+ end
642
791
  rescue Exception => ex
643
792
  raise "AttributesMergeError #{ex.message}"
644
793
  end
@@ -657,7 +806,6 @@ def derive_backend type, out_file
657
806
  end
658
807
 
659
808
  def render_doc doc, build
660
- build.set("backend", derive_backend(doc.type, build.output) ) unless build.backend
661
809
  case build.backend
662
810
  when "html5", "pdf"
663
811
  asciidocify(doc, build)
@@ -734,15 +882,15 @@ def generate_site doc, build
734
882
  attrs.merge!(build.attributes) if build.attributes
735
883
  attrs = {"asciidoctor" => {"attributes" => attrs} }
736
884
  attrs_yaml = attrs.to_yaml # Convert it all back to Yaml, as we're going to write a file to feed back to Jekyll
737
- FileUtils::mkdir_p("build/pre") unless File.exists?("build/pre")
738
- File.open("build/pre/_attributes.yml", 'w') { |file| file.write(attrs_yaml) }
739
- build.add_config_file("build/pre/_attributes.yml")
885
+ File.open("#{@build_dir}/pre/_attributes.yml", 'w') { |file| file.write(attrs_yaml) }
886
+ build.add_config_file("#{@build_dir}/pre/_attributes.yml")
740
887
  config_list = build.prop_files_array.join(',') # flatten the Array back down for the CLI
741
888
  opts_args = ""
889
+ quiet = "--quiet" if @quiet || @explicit
742
890
  if build.props['arguments']
743
891
  opts_args = build.props['arguments'].to_opts_args
744
892
  end
745
- command = "bundle exec jekyll build --config #{config_list} #{opts_args}"
893
+ command = "bundle exec jekyll build --config #{config_list} #{opts_args} #{quiet}"
746
894
  end
747
895
  @logger.info "Running #{command}"
748
896
  @logger.debug "AsciiDoc attributes: #{doc.attributes.to_yaml} "
@@ -839,6 +987,8 @@ end
839
987
 
840
988
  # Extending Liquid filters/text manipulation
841
989
  module CustomFilters
990
+ include Jekyll::Filters
991
+
842
992
  def plainwrap input
843
993
  input.wrap
844
994
  end
@@ -875,7 +1025,7 @@ Liquid::Template.register_filter(CustomFilters)
875
1025
  command_parser = OptionParser.new do|opts|
876
1026
  opts.banner = "Usage: liquidoc [options]"
877
1027
 
878
- opts.on("-a KEY=VALUE", "For passing an AsciiDoc attribute parameter to Asciidoctor. Ex: -a basedir=some/path -a custom_var='my value'") do |n|
1028
+ opts.on("-a KEY=VALUE", "For passing an AsciiDoc attribute parameter to Asciidoctor. Ex: -a imagesdir=some/path -a custom_var='my value'") do |n|
879
1029
  pair = {}
880
1030
  k,v = n.split('=')
881
1031
  pair[k] = v
@@ -884,7 +1034,11 @@ command_parser = OptionParser.new do|opts|
884
1034
 
885
1035
  # Global Options
886
1036
  opts.on("-b PATH", "--base=PATH", "The base directory, relative to this script. Defaults to `.`, or pwd." ) do |n|
887
- @data_file = @base_dir + n
1037
+ @base_dir = n
1038
+ end
1039
+
1040
+ 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|
1041
+ @build_dir = n
888
1042
  end
889
1043
 
890
1044
  opts.on("-c", "--config=PATH", "Configuration file, enables preset source, template, and output.") do |n|
@@ -911,17 +1065,25 @@ command_parser = OptionParser.new do|opts|
911
1065
  @template_file = @base_dir + n
912
1066
  end
913
1067
 
914
- opts.on("--verbose", "Run verbose") do |n|
1068
+ opts.on("--verbose", "Run verbose debug logging.") do |n|
915
1069
  @logger.level = Logger::DEBUG
916
1070
  @verbose = true
917
1071
  end
918
1072
 
919
- opts.on("--stdout", "Puts the output in STDOUT instead of writing to a file.") do
920
- @output_type = "stdout"
1073
+ opts.on("--quiet", "Run with only WARN- and error-level logs written to console.") do |n|
1074
+ @logger.level = Logger::WARN
1075
+ @quiet = true
1076
+ end
1077
+
1078
+ opts.on("--explicit", "Log explicit step descriptions to console as build progresses. (Otherwise writes to file at #{@build_dir}/pre/config-explainer.adoc .)") do |n|
1079
+ explainer_init("STDOUT")
1080
+ @explainer.level = Logger::INFO
1081
+ @logger.level = Logger::WARN # Suppress all those INFO-level messages
1082
+ @explicit = true
921
1083
  end
922
1084
 
923
- opts.on("--clean PATH", "Force deletes the designated directory and all its contents WITHOUT WARNING.") do |n|
924
- @clean_dir = n
1085
+ opts.on("--stdout", "Puts the output in STDOUT instead of writing to a file.") do
1086
+ @output_type = "stdout"
925
1087
  end
926
1088
 
927
1089
  opts.on("--deploy", "EXPERIMENTAL: Trigger a jekyll serve operation against the destination dir of a Jekyll render step.") do
@@ -938,21 +1100,21 @@ end
938
1100
  command_parser.parse!
939
1101
 
940
1102
  # Upfront debug output
941
- @logger.debug "Base dir: #{@base_dir}"
942
- @logger.debug "Config file: #{@config_file}"
1103
+ @logger.debug "Base dir: #{@base_dir} (The path from which LiquiDoc CLI commands are relative.)"
1104
+
1105
+ explainer_init
943
1106
 
944
1107
  # ===
945
1108
  # Execute
946
1109
  # ===
947
- if @clean_dir
948
- FileUtils.remove_dir(@clean_dir)
949
- end
1110
+
950
1111
  unless @config_file
1112
+ @logger.debug "Executing config-free build based on API/CLI arguments alone."
951
1113
  if @data_file
952
1114
  liquify(@data_file, @template_file, @output_file)
953
1115
  end
954
1116
  if @index_file
955
- @logger.warn "Publishing via command line arguments not yet implemented. Use a config file."
1117
+ @logger.warn "Rendering via command line arguments is not yet implemented. Use a config file."
956
1118
  end
957
1119
  else
958
1120
  @logger.debug "Executing... config_build"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: liquidoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Dominick
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-05 00:00:00.000000000 Z
11
+ date: 2018-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -51,7 +51,6 @@ files:
51
51
  - bin/liquidoc
52
52
  - lib/liquidoc.rb
53
53
  - lib/liquidoc/version.rb
54
- - lib/yaml_plus.rb
55
54
  homepage: https://github.com/scalingdata/liquidoc
56
55
  licenses:
57
56
  - MIT
data/lib/yaml_plus.rb DELETED
@@ -1,60 +0,0 @@
1
- require 'safe_yaml/load'
2
- module YamlPlus
3
-
4
- def self.parse data
5
- puts data
6
- return
7
- # self.new.load((::SafeYAML.load raw_data), theme_data)
8
- # input_data ||= ::OpenStruct.new
9
- # return input_data unless ::Hash === data
10
- # data.inject(input_data) {|data, (key, val)| process_entry key, val, data }
11
- # input_data.base_align ||= 'left'
12
- # input_data
13
- end
14
-
15
- def process_entry key, val, data
16
- if ::Hash === val
17
- val.each do |key2, val2|
18
- process_entry %(#{key}_#{key2.tr '-', '_'}), val2, data
19
- end
20
- else
21
- data[key] = evaluate val, data
22
- end
23
- data
24
- end
25
-
26
- def evaluate expr, vars
27
- case expr
28
- when ::String
29
- evaluate_math(expand_vars expr, vars)
30
- when ::Array
31
- expr.map {|e| evaluate e, vars }
32
- else
33
- expr
34
- end
35
- end
36
-
37
- def expand_vars expr, vars
38
- if (idx = (expr.index '$'))
39
- if idx == 0 && expr =~ LoneVariableRx
40
- if vars.respond_to? $1
41
- vars[$1]
42
- else
43
- warn %(asciidoctor: WARNING: unknown variable reference: $#{$1})
44
- expr
45
- end
46
- else
47
- expr.gsub(VariableRx) {
48
- if vars.respond_to? $1
49
- vars[$1]
50
- else
51
- warn %(asciidoctor: WARNING: unknown variable reference: $#{$1})
52
- $&
53
- end
54
- }
55
- end
56
- else
57
- expr
58
- end
59
- end
60
- end