Top Level Namespace

Defined Under Namespace

Modules: CustomFilters, Liquidoc Classes: AsciiDoctorConfig, AsciiDocument, Build, BuildConfig, BuildConfigStep, DataSrc, String

Instance Method Summary collapse

Instance Method Details

#asciidocify(doc, build) ⇒ Object



574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
# File 'lib/liquidoc.rb', line 574

def asciidocify doc, build
  @logger.debug "Executing Asciidoctor render operation for #{build.output}."
  to_file = build.output
  unless doc.type == build.doctype
    if build.doctype.nil?
      build.set("doctype", doc.type)
    end
  end
  back = derive_backend(doc.type, build.output)
  unless build.style.nil?
    case back
    when "pdf"
      doc.add_attrs!({"pdf-style"=>build.style})
    when "html5"
      doc.add_attrs!({"stylesheet"=>build.style})
    else
      raise "UnrecognizedBackend"
    end
  end
  # Add attributes from config file build section
  doc.add_attrs!(build.attributes.to_h)
  # Add attributes from command-line -a args
  doc.add_attrs!(@passed_attrs)
  @logger.debug "Final pre-parse attributes: #{doc.attributes}"
  # Perform the aciidoctor convert
  unless back == "pdf"
    Asciidoctor.convert_file(
      doc.index,
      to_file: to_file,
      attributes: doc.attributes,
      require: "pdf",
      backend: back,
      doctype: build.doctype,
      safe: "unsafe",
      sourcemap: true,
      verbose: @verbose,
      mkdirs: true
    )
  else # For PDFs, we're calling the asciidoctor-pdf CLI, as the main dependency doesn't seem to perform the same way
    attributes = '-a ' + doc.attributes.map{|k,v| "#{k}='#{v}'"}.join(' -a ')
    command = "asciidoctor-pdf -o #{to_file} -b pdf -d #{build.doctype} -S unsafe #{attributes} -a no-header-footer --trace #{doc.index}"
    @logger.debug "Running #{command}"
    system command
  end
  @logger.info "Rendered file #{to_file}."
end

#config_build(config_file) ⇒ Object

Establish source, template, index, etc details for build jobs from a config file



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/liquidoc.rb', line 58

def config_build config_file
  @logger.debug "Using config file #{config_file}."
  validate_file_input(config_file, "config")
  begin
    config = YAML.load_file(config_file)
  rescue
    unless File.exists?(config_file)
      @logger.error "Config file #{config_file} not found."
    else
      @logger.error "Problem loading config file #{config_file}. Exiting."
    end
    raise "ConfigFileError"
  end
  cfg = BuildConfig.new(config) # convert the config file to a new object called 'cfg'
  iterate_build(cfg)
end

#copy_assets(src, dest, inclusive = true) ⇒ Object

Copy images and other files into target dir



502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
# File 'lib/liquidoc.rb', line 502

def copy_assets src, dest, inclusive=true
  if File.file?(src) # for sources that are files
    target_dir = File.dirname(dest)
  else # if src is a directory
    unless inclusive then src = src + "/." end
    target_dir = dest
  end
  @logger.debug "Copying #{src} to #{dest}"
  begin
    FileUtils.mkdir_p(target_dir) unless File.directory?(target_dir)
    if File.directory?(src)
      FileUtils.cp_r(src, dest)
    else
      FileUtils.cp(src, dest)
    end
    @logger.info "Copied #{src} to #{dest}."
  rescue Exception => ex
    @logger.warn "Problem while copying assets. #{ex.message}"
    raise
  end
end

#derive_backend(type, out_file) ⇒ Object



564
565
566
567
568
569
570
571
572
# File 'lib/liquidoc.rb', line 564

def derive_backend type, out_file
  case File.extname(out_file)
  when ".pdf"
    backend = "pdf"
  else
    backend = "html5"
  end
  return backend
end

#ingest_attributes(attr_file) ⇒ Object

Gather attributes from one or more fixed attributes files



529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
# File 'lib/liquidoc.rb', line 529

def ingest_attributes attr_file
  file_array = attr_file.split(",")
  attrs = {}
  for f in file_array
    if f.include? ":"
      file = f.split(":")
      filename = file[0]
      block_name = file[1]
    else
      filename = f
      block_name = false
    end
    validate_file_input(filename, "attributes")
    begin
      new_attrs = YAML.load_file(filename)
      if block_name
        begin
          new_attrs = new_attrs[block_name]
        rescue
          raise "InvalidAttributesBlock"
        end
      end
    rescue Exception => ex
      @logger.error "Attributes block invalid. #{ex.class}: #{ex.message}"
      raise "AttributeBlockError"
    end
    begin
      attrs.merge!new_attrs
    rescue Exception => ex
      raise "AttributesMergeError #{ex.message}"
    end
  end
  return attrs
end

#ingest_data(datasrc) ⇒ Object

Pull in a semi-structured data file, converting contents to a Ruby hash



382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# File 'lib/liquidoc.rb', line 382

def ingest_data datasrc
# Must be passed a proper data object (there must be a better way to validate arg datatypes)
  unless datasrc.is_a? Object
    raise "InvalidDataObject"
  end
  # This proc should really begin here, once the datasrc object is in order
  case datasrc.type
  when "yml"
    begin
      data = YAML.load_file(datasrc.file)
    rescue Exception => ex
      @logger.error "There was a problem with the data file. #{ex.message}"
    end
  when "json"
    begin
      data = JSON.parse(File.read(datasrc.file))
    rescue Exception => ex
      @logger.error "There was a problem with the data file. #{ex.message}"
    end
  when "xml"
    begin
      data = Crack::XML.parse(File.read(datasrc.file))
      data = data['root']
    rescue Exception => ex
      @logger.error "There was a problem with the data file. #{ex.message}"
    end
  when "csv"
    output = []
    i = 0
    begin
      CSV.foreach(datasrc.file, headers: true, skip_blanks: true) do |row|
        output[i] = row.to_hash
        i = i+1
      end
      output = {"data" => output}
      data = output
    rescue
      @logger.error "The CSV format is invalid."
    end
  when "regex"
    if datasrc.pattern
      data = parse_regex(datasrc.file, datasrc.pattern)
    else
      @logger.error "You must supply a regex pattern with your free-form data file."
      raise "MissingRegexPattern"
    end
  end
end

#iterate_build(cfg) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/liquidoc.rb', line 75

def iterate_build cfg
  stepcount = 0
  for step in cfg.steps # iterate through each node in the 'config' object, which should start with an 'action' parameter
    stepcount = stepcount + 1
    step = BuildConfigStep.new(step) # create an instance of the Action class, validating the top-level step hash (now called 'step') in the process
    type = step.type
    case type # a switch to evaluate the 'action' parameter for each step in the iteration...
    when "parse"
      data = DataSrc.new(step.data)
      builds = step.builds
      for bld in builds
        build = Build.new(bld, type) # create an instance of the Build class; Build.new accepts a 'bld' hash & action 'type'
        liquify(data, build.template, build.output) # perform the liquify operation
      end
    when "migrate"
      inclusive = true
      inclusive = step.options['inclusive'] if defined?(step.options['inclusive'])
      copy_assets(step.source, step.target, inclusive)
    when "render"
      if defined?(step.data) # if we're passing attributes as a YAML file, let's ingest that up front
        attrs = ingest_attributes(step.data)
      else
        attrs = {}
      end
      validate_file_input(step.source, "source")
      doc = AsciiDocument.new(step.source)
      doc.add_attrs!(attrs)
      builds = step.builds
      for bld in builds
        build = Build.new(bld, type) # create an instance of the Build class; Build.new accepts a 'bld' hash & action 'type'
        asciidocify(doc, build) # perform the liquify operation
      end
    when "deploy"
      @logger.warn "Deploy actions not yet implemented."
    else
      @logger.warn "The action `#{type}` is not valid."
    end
  end
end

#liquify(datasrc, template_file, output) ⇒ Object

Parse given data using given template, generating given output



458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
# File 'lib/liquidoc.rb', line 458

def liquify datasrc, template_file, output
  @logger.debug "Executing liquify parsing operation."
  if datasrc.is_a? String
    datasrc = DataSrc.new(datasrc)
  end
  validate_file_input(datasrc.file, "data")
  validate_file_input(template_file, "template")
  data = ingest_data(datasrc)
  begin
    template = File.read(template_file) # reads the template file
    template = Liquid::Template.parse(template) # compiles template
    rendered = template.render(data) # renders the output
  rescue Exception => ex
    message = "Problem rendering Liquid template. #{template_file}\n" \
      "#{ex.class} thrown. #{ex.message}"
    @logger.error message
    raise message
  end
  unless output.downcase == "stdout"
    output_file = output
    base_path = File.dirname(output)
    begin
      FileUtils::mkdir_p(base_path) unless File.exists?(base_path)
      File.open(output_file, 'w') { |file| file.write(rendered) } # saves file
    rescue Exception => ex
      @logger.error "Failed to save output.\n#{ex.class} #{ex.message}"
      raise "FileNotBuilt"
    end
    if File.exists?(output_file)
      @logger.info "File built: #{File.basename(output_file)}"
    else
      @logger.error "Hrmp! File not built."
      raise "FileNotBuilt"
    end
  else # if stdout
    puts "========\nOUTPUT: Rendered with template #{template_file}:\n\n#{rendered}\n"
  end
end

#parse_regex(data_file, pattern) ⇒ Object



431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'lib/liquidoc.rb', line 431

def parse_regex data_file, pattern
  records = []
  pattern_re = /#{pattern}/
  @logger.debug "Using regular expression #{pattern} to parse data file."
  groups = pattern_re.names
  begin
    File.open(data_file, "r") do |file_proc|
      file_proc.each_line do |row|
        matches = row.match(pattern_re)
        if matches
          row_h = {}
          groups.each do |var| # loop over the named groups, adding their key & value to the row_h hash
            row_h.merge!(var => matches[var])
          end
          records << row_h # add the row to the records array
        end
      end
    end
    output = {"data" => records}
  rescue Exception => ex
    @logger.error "Something went wrong trying to parse the free-form file. #{ex.class} thrown. #{ex.message}"
    raise "Freeform parse error"
  end
  return output
end

#validate_config_structure(config) ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/liquidoc.rb', line 132

def validate_config_structure config
  unless config.is_a? Array
    message =  "The configuration file is not properly structured."
    @logger.error message
    raise "ConfigStructError"
  else
    if (defined?(config['action'])).nil?
      message =  "Every listing in the configuration file needs an action type declaration."
      @logger.error message
      raise "ConfigStructError"
    end
  end
# TODO More validation needed
end

#validate_file_input(file, type) ⇒ Object

Verify files exist



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/liquidoc.rb', line 116

def validate_file_input file, type
  @logger.debug "Validating input file for #{type} file #{file}"
  error = false
  unless file.is_a?(String) and !file.nil?
    error = "The #{type} filename (#{file}) is not valid."
  else
    unless File.exists?(file)
      error = "The #{type} file (#{file}) was not found."
    end
  end
  if error
    @logger.error "Could not validate input file: #{error}"
    raise "InvalidInput"
  end
end