Skip to content

Commit f593bf2

Browse files
Parser upgrades alt -- redo 0.9.0 (#51)
* Revert "Add dynamic config & allow vars for non-config builds (#49)" This reverts commit 006f16f. We want to reapply these changes and reconcile the conflicts without overwriting anything. * Merge --cvar into --var * bugfix: handle adding variables to emptyset * Bump verion to 0.9.1
1 parent e5787b1 commit f593bf2

3 files changed

Lines changed: 164 additions & 37 deletions

File tree

README.adoc

Lines changed: 92 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
= LiquiDoc
2+
:toc: preamble
3+
24
// tag::overview[]
35
LiquiDoc is a documentation build utility for true single-sourcing of technical content and data.
46
It is especially suited for documentation projects with various required output formats, but it is intended for any project with complex, versioned input data for use in docs, user interfaces, and even back-end code.
@@ -876,24 +878,92 @@ See <<per-build-properties-files>>.
876878

877879
=== Deploy Operations
878880

879-
It's not clear how deeply we will delve into deploy operations, since other build systems (such as rake) would seem far more suitable.
880-
For testing purposes, however, spinning up a local webserver with the same stroke that you build a site is pretty rewarding and time saving.
881+
Mainstream deployment platforms are probebly better suited to tying all your operations together, but we plan to bake a few common operations in to help you get started.
882+
For true build-and-deployment control, consider build tools such as Make, Rake, and Gradle, or deployment tools like Travis CI, CircleCI, and Jenkins.
883+
884+
==== Jekyll Serve
885+
886+
For testing purposes, however, spinning up a local webserver with the same stroke that you build a site is pretty rewarding and time saving, so we'll start there.
881887

882888
For now, this functionality is limited to adding a `--deploy` flag to your `liquidoc` command.
883889
This will attempt to serve files from the *destination* set for the associated Jekyll build.
884890

885891
[WARNING]
886892
Deployment of Jekyll sites is both limited and untested under nonstandard conditions.
887893

894+
==== Algolia Search Indexing for Jekyll
895+
896+
If you're using Jekyll to build sites, LiquiDoc makes indexing your files with the Algolia cloud search service a matter of configuration.
897+
The heavy lifting is performed by the jekyll-algolia plugin, but LiquiDoc can handle indexing even a complex site by using the same configuration that built your HTML content (which is what Algolia actually indexes).
898+
888899
[NOTE]
889-
The <<config-settings-matrix,*Config Settings Matrix*>> has moved to the <<reference,Reference Section>>.
900+
You will need a free community (or premium) link:https://www.algolia.com/users/sign_up/hacker[Algolia account] to take advantage of Algolia's indexing service and REST API.
901+
Simply create a named index, then visit the API Keys to collect the rest of the info you'll need to get going.
902+
903+
Two hard-coding steps are required to prep your source to handle Algolia index pushes.
904+
905+
. Add a block to your main Jekyll configuration file.
906+
+
907+
.Example Jekyll Algolia configuration
908+
[source,yaml]
909+
----
910+
algolia:
911+
application_id: 'your-application-id' # <1>
912+
search_only_api_key: 'your-search-only-api-key' # <2>
913+
extensions_to_index: [adoc] # <3>
914+
----
915+
+
916+
<1> From the top bar of your Algolia interface.
917+
<2> From the API Keys screen of your Algolia interface.
918+
<3> List as many extensions as apply, separated by commas.
919+
920+
. Add a block to your build config.
921+
+
922+
[source,yaml]
923+
----
924+
- action: render
925+
data: globals.yml
926+
builds:
927+
- backend: jekyll
928+
properties:
929+
files:
930+
- _configs/jekyll-global.yml
931+
- _configs/jekyll-portal-1.yml
932+
arguments:
933+
destination: build/site/user-basic
934+
attributes:
935+
portal_term: Guide
936+
search:
937+
index: 'portal-1'
938+
----
939+
+
940+
The `index:` parameter is for the name of the index you are pushing to.
941+
(An Algolia “app” can have multiple “indices”.)
942+
If you have
943+
944+
Now you can call your same LiquiDoc build command with the `--search-index-push` or `--search-index-dry` flags along with the `--search-api-key='your-admin-api-key-here'` argument in order to invoke the indexing operation.
945+
The `--search-index-dry` flag merely tests content packaging, whereas `--search-index-push` connects to the Algolia REST API and attempt to push your content for indexing and storage.
946+
947+
.Example Jekyll Algolia deployment
948+
[source,shell]
949+
----
950+
bundle exec liquidoc -c _configs/build-docs.yml --search-index-push --search-index-api-key='90f556qaa456abh6j3w7e8c10t48c2i57'
951+
----
952+
953+
This operation performs a complete build, including each render operation, before the Algolia plugin processes content and pushes each build to the indexing service, in turn.
954+
955+
[TIP]
956+
To add modern site search for your users, add link:https://community.algolia.com/instantsearch.js/[Algolia's InstantSearch functionality] to your front end!
890957

891958
== Configuring a LiquiDoc Build
892959

893960
In order to seriously explore and instruct this tool's ability to single-source a product's entire docs-integrated codebase, I have set out on a project for LiquiDoc to eat its own proverbial dog food.
894961
That is, I'm using LiquiDoc to document LiquiDoc in a separate repository -- one which treats the LiquiDoc gem repository (_this_ repo) as a Git submodule.
895962
That is, for purposes of coding, _all_ of the *liquidoc-gem* repo's code is accesible by way of an alias path, `products/liquidoc-gem`, from the base of the *liquidoc-docs* project.
896963

964+
[NOTE]
965+
The <<config-settings-matrix,*Config Settings Matrix*>> has moved to the <<reference,Reference Section>>.
966+
897967
=== Gem Repo as Submodule
898968

899969
I am on record as prefering to _keep docs source in the same codebase as that of the product they reference_.
@@ -988,8 +1058,8 @@ That is, your config files can contain variables, conditionals, and iterative lo
9881058
All you have to do is (1) add Liquid tags to your YAML configuration file and (2) either (a) pass at least one _config variable_ to it when running your `liquidoc` command or (b) pass it the `--parseconfig` flag.
9891059

9901060
Let's explore that second requirement.
991-
If the Liquid markup in your config file expects variables, pass those variables on the `liquidoc` CLI using `--configvar key=value`.
992-
Otherwise, if you are not passing variables to your config, instruct LiquiDoc to parse the config file using the `parseconfig` CLI option.
1061+
If the Liquid markup in your config file expects variables, pass those variables on the `liquidoc` CLI using `--var key=value`.
1062+
Otherwise, if you are not passing variables to your config, instruct LiquiDoc to parse the config file using the `--parseconfig` CLI flag.
9931063
For example, this might be the case if your config merely contains some simple looping functionality to process lots of files.
9941064

9951065
[[config-variables]]
@@ -1011,16 +1081,19 @@ Let's first take a look at a sample dynamic configuration to see if we can under
10111081

10121082
This config file wants to build a product datasheet for a specific product, which it expects to be indicated by a config variable called `product_slug`.
10131083

1014-
Config variables are passed using the `--configvar varname='var val'` format, where `varname` is any key that exists as a Liquid variable in your config file, and `'var val'` is its value, wrapped in single quotes.
1084+
Config variables are passed using the `--var varname='var val'` format, where `varname` is any key that exists as a Liquid variable in your config file, and `'var val'` is its value, wrapped in single quotes.
10151085
Let's say in this case, we want to generate the datasheet for the Windows Enterprise edition of our product.
10161086

10171087
[source,shell]
10181088
----
1019-
bundle exec liquidoc -c _configs/build-config.yml --configvar product_slug=win-ent
1089+
bundle exec liquidoc -c _configs/build-config.yml -v product_slug=win-ent
10201090
----
10211091

1022-
This will cause our dynamic configuration to look for a data block formatted like so: `data/products.yml:product_win-ent`.
1023-
So long as our `products.yml` file contains a top-level data structure called `product_win-ent`, we're off to the races.
1092+
[NOTE]
1093+
The `-v` option is an alias for `--var`.
1094+
1095+
This will cause our dynamic configuration to look for a data block formatted like so: `data/products.yml:win-ent`.
1096+
So long as our `products.yml` file contains a top-level data structure called `win-ent`, we're off to the races.
10241097

10251098
==== Eliminating Config Variables
10261099

@@ -1080,7 +1153,7 @@ LiquiDoc {{ vars.product.edition }}
10801153
</ul>
10811154
----
10821155

1083-
To set the values of `vars.edition` and `vars.env` in the config file, add for instance `--configvar edition=basic --configvar env=staging`
1156+
To set the values of `vars.edition` and `vars.env` in the config file, add for instance `--var edition=basic --var env=staging`
10841157

10851158
==== Constraining Build Options with Dynamic Configuration
10861159

@@ -1113,7 +1186,7 @@ For instance,
11131186
{% endif %}
11141187
----
11151188

1116-
With a build config like this, optionally invoking `--configvar recipe=nopdf`, for instance, will suppress the PDF substep during the build routine.
1189+
With a build config like this, optionally invoking `--var recipe=nopdf`, for instance, will suppress the PDF substep during the build routine.
11171190

11181191
==== Generating Starter Files with Dynamic Configs
11191192

@@ -1125,7 +1198,7 @@ Once you're comfortable with the concept of <<dynamic-config,dynamic LiquiDoc co
11251198
.Example liquidoc execution with a dynamic config file
11261199
[source,shell]
11271200
----
1128-
bundle exec liquidoc -c _configs/init_topic.yml --configvar slug=some_c_slug-string --configvar title='Some Topic Title for Publication'
1201+
bundle exec liquidoc -c _configs/init_topic.yml --var slug=some_c_slug-string --var title='Some Topic Title for Publication'
11291202
----
11301203

11311204
The example above commands an extraordinary LiquiDoc build routine.
@@ -1149,7 +1222,7 @@ The configuration file, `init_topic.yml`, creates topic-file stubs and schema-fi
11491222
----
11501223

11511224
As you can see, since this file has Liquid variables embedded in it, we must pass those variables during CLI execution in order for the build to work at all.
1152-
This config file is parsed just like any standard parse action, though it uses the `--configvar` option to ingest environment variables, scoped as `vars.` in the template (remember, in this case _the config file itself is the template_).
1225+
This config file is parsed just like any standard parse action, though it uses the `--var` option to ingest environment variables, scoped as `vars.` in the template (remember, in this case _the config file itself is the template_).
11531226
Parsing the configuration will essentially be the first act of the configured build routine, which will then run the parsed file, step by step.
11541227

11551228
.Example _parsed_ config file for topic-file stub generation
@@ -1303,6 +1376,12 @@ s| properties
13031376
| N/A
13041377
| Optional
13051378
|
1379+
1380+
s| search
1381+
| N/A
1382+
| N/A
1383+
| Optional
1384+
|
13061385
|===
13071386

13081387
pass:[*]The `output` setting is considered optional for render operations because static site generations target a directory set in the SSG's config file.

lib/liquidoc.rb

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
require 'liquidoc'
2+
require 'optparse'
23
require 'yaml'
34
require 'json'
4-
require 'optparse'
55
require 'liquid'
66
require 'asciidoctor'
77
require 'asciidoctor-pdf'
@@ -51,6 +51,8 @@
5151
@verbose = false
5252
@quiet = false
5353
@explicit = false
54+
@search_index = false
55+
@search_index_dry = ''
5456

5557
# Instantiate the main Logger object, which is always running
5658
@logger = Logger.new(STDOUT)
@@ -344,6 +346,7 @@ def initialize build, type
344346
build['props'] = build['properties'] if build['properties']
345347
@build = build
346348
@type = type
349+
@build['variables'] = {}
347350
end
348351

349352
def template
@@ -445,6 +448,18 @@ def prop_files_array
445448
# props['files'].force_array if props['files']
446449
# end
447450

451+
def search
452+
props['search']
453+
end
454+
455+
def add_search_prop! prop
456+
begin
457+
self.search.merge!prop
458+
rescue
459+
raise "PropertyInsertionError"
460+
end
461+
end
462+
448463
# NOTE this section repeats in Class.AsciiDocument
449464
def attributes
450465
@build['attributes']
@@ -609,11 +624,7 @@ def get_data datasrc
609624

610625
# Pull in a semi-structured data file, converting contents to a Ruby hash
611626
def ingest_data datasrc
612-
# Must be passed a proper data object (there must be a better way to validate arg datatypes)
613-
unless datasrc.is_a? Object
614-
raise "InvalidDataObject"
615-
end
616-
# This proc should really begin here, once the datasrc object is in order
627+
raise "InvalidDataObject" unless datasrc.is_a? Object
617628
case datasrc.type
618629
when "yml"
619630
begin
@@ -677,7 +688,7 @@ def parse_regex data_file, pattern
677688
end
678689
end
679690
end
680-
output = {"data" => records}
691+
output = records
681692
rescue Exception => ex
682693
@logger.error "Something went wrong trying to parse the free-form file. #{ex.class} thrown. #{ex.message}"
683694
raise "Freeform parse error"
@@ -911,8 +922,8 @@ def generate_site doc, build
911922
when "jekyll"
912923
attrs = doc.attributes
913924
build.add_config_file("_config.yml") unless build.prop_files_array
914-
jekyll_config = YAML.load_file(build.prop_files_array[0]) # load the first Jekyll config file locally
915-
attrs.merge! ({"base_dir" => jekyll_config['source']}) # Sets default Asciidoctor base_dir to == Jekyll root
925+
jekyll = load_jekyll_data(build) # load the first Jekyll config file locally
926+
attrs.merge! ({"base_dir" => jekyll['source']}) # Sets default Asciidoctor base_dir to == Jekyll root
916927
# write all AsciiDoc attributes to a config file for Jekyll to ingest
917928
attrs.merge!(build.attributes) if build.attributes
918929
attrs = {"asciidoctor" => {"attributes" => attrs} }
@@ -925,14 +936,31 @@ def generate_site doc, build
925936
if build.props['arguments']
926937
opts_args = build.props['arguments'].to_opts_args
927938
end
928-
command = "bundle exec jekyll build --config #{config_list} #{opts_args} #{quiet}"
939+
base_args = "--config #{config_list} #{opts_args}"
940+
command = "bundle exec jekyll build #{base_args} #{quiet}"
941+
if @search_index
942+
# TODO enable config-based admin api key ingest once config is dynamic
943+
command = algolia_index_cmd(build, @search_api_key, base_args)
944+
@logger.warn "Search indexing failed." unless command
945+
end
946+
end
947+
if command
948+
@logger.info "Running #{command}"
949+
@logger.debug "AsciiDoc attributes: #{doc.attributes.to_yaml} "
950+
system command
929951
end
930-
@logger.info "Running #{command}"
931-
@logger.debug "AsciiDoc attributes: #{doc.attributes.to_yaml} "
932-
system command
933952
jekyll_serve(build) if @jekyll_serve
934953
end
935954

955+
def load_jekyll_data build
956+
data = {}
957+
build.prop_files_array.each do |file|
958+
settings = YAML.load_file(file)
959+
data.merge!settings if settings
960+
end
961+
return data
962+
end
963+
936964
# ===
937965
# DEPLOY procs
938966
# ===
@@ -948,6 +976,20 @@ def jekyll_serve build
948976
system command
949977
end
950978

979+
def algolia_index_cmd build, apikey=nil, args
980+
unless build.search and build.search['index']
981+
@logger.warn "No index configuration found for build; jekyll-algolia operation skipped for this build."
982+
return false
983+
else
984+
unless apikey
985+
@logger.warn "No Algolia admin API key passed; skipping jekyll-algolia operation for this build."
986+
return false
987+
else
988+
return "ALGOLIA_INDEX_NAME='#{build.search['index']}' ALGOLIA_API_KEY='#{apikey}' bundle exec jekyll algolia #{@search_index_dry} #{args} "
989+
end
990+
end
991+
end
992+
951993
# ===
952994
# Text manipulation Classes, Modules, procs, etc
953995
# ===
@@ -1115,7 +1157,7 @@ def regexreplace input, regex, replacement=''
11151157
@quiet = true
11161158
end
11171159

1118-
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|
1160+
opts.on("--explain", "Log explicit step descriptions to console as build progresses. (Otherwise writes to file at #{@build_dir}/pre/config-explainer.adoc .)") do |n|
11191161
explainer_init("STDOUT")
11201162
@explainer.level = Logger::INFO
11211163
@logger.level = Logger::WARN # Suppress all those INFO-level messages
@@ -1130,21 +1172,27 @@ def regexreplace input, regex, replacement=''
11301172
@jekyll_serve = true
11311173
end
11321174

1133-
opts.on("--var KEY=VALUE", "For passing variables directly to the 'vars.' scope template via command line, for non-config builds only.") do |n|
1134-
pair = {}
1135-
k,v = n.split('=')
1136-
pair[k] = v
1137-
@passed_vars.merge!pair
1175+
opts.on("--search-index-push", "Runs any search indexing configured in the build step and pushes to Algolia.") do
1176+
@search_index = true
1177+
end
1178+
1179+
opts.on("--search-index-dry", "Runs any search indexing configured in the build step but does NOT push to Algolia.") do
1180+
@search_index = true
1181+
@search_index_dry = "--dry-run"
1182+
end
1183+
1184+
opts.on("--search-api-key=STRING", "Passes Algolia Admin API key (which you should keep out of Git).") do |n|
1185+
@search_api_key = n
11381186
end
11391187

1140-
opts.on("-x", "--cvar KEY=VALUE", "For sending variables to the 'vars.' scope of the config file and triggering Liquid parsing of config.") do |n|
1188+
opts.on("-v", "--var KEY=VALUE", "For passing variables directly to the 'vars.' scope of a template; for dynamic configs, too.") do |n|
11411189
pair = {}
11421190
k,v = n.split('=')
11431191
pair[k] = v
1144-
@passed_configvars.merge!pair
1192+
@passed_vars.merge!pair
11451193
end
11461194

1147-
opts.on("--parse-config", "Preprocess the designated configuration file as a Liquid template. Superfluous when passing -x/--cvar arguments.") do
1195+
opts.on("--parse-config", "Preprocess the designated configuration file as a Liquid template. Superfluous when passing -v/--var arguments.") do
11481196
@parseconfig = true
11491197
end
11501198

@@ -1176,5 +1224,5 @@ def regexreplace input, regex, replacement=''
11761224
end
11771225
else
11781226
@logger.debug "Executing... config_build"
1179-
config_build(@config_file, @passed_configvars, @parseconfig)
1227+
config_build(@config_file, @passed_vars, @parseconfig)
11801228
end

lib/liquidoc/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module Liquidoc
2-
VERSION = "0.9.0"
2+
VERSION = "0.9.1"
33
end

0 commit comments

Comments
 (0)