liquidoc 0.2.0 → 0.3.0

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/liquidoc.rb +311 -133
  3. data/lib/liquidoc/version.rb +1 -1
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 84d0dce2c5baddc6b4b2c9d7a9dffe763ce32a73
4
- data.tar.gz: 65d945dcfdb8480a72aa72650adf4e509e7834ed
3
+ metadata.gz: 9adb50baa6b3a7c4f31b2de0cc7888bdb69e798f
4
+ data.tar.gz: '0392150cfd9c8a5b2ea35ac16116e1ae519bd874'
5
5
  SHA512:
6
- metadata.gz: d8e16dd3ae8c7851c1e7f24d0b5a684019d9b7f8e3b93efb8b8713b243e7692ef53150db619e34dc3247f4861e9dae335152aad51f1b754795cb95e59a1cfde8
7
- data.tar.gz: a72ffb5946651195885fe70e25739c2476939fd9ebdfd637d96ef56896332fcde81b6cce7555ff311ef79c05890580f1940bfd2c264067fe5a6ae304e9b60d0a
6
+ metadata.gz: 2762bc4215573e07f60bcc88d91c2fd2b62dc9995be94ac29b89fd96ec9f214d4135f6f7d66239b99acf3ee4fff57b21d3a599e54d14298073cd287ac4b96a95
7
+ data.tar.gz: b0c252cbc74af8f56ffdd03718713a7a193d9da94caa110cc68abd5a7f58e917ea611031b939aaca8a456a370f8da3a3fc13e770605279a60dbad22fff681c84
@@ -8,15 +8,32 @@ require 'logger'
8
8
  require 'csv'
9
9
  require 'crack/xml'
10
10
 
11
+ # ===
12
+ # Table of Contents
13
+ # ===
14
+ #
15
+ # 1. dependencies stack
16
+ # 2. default settings
17
+ # 3. general methods
18
+ # 4. object classes
19
+ # 5. action-specific methods
20
+ # 5a. parse methods
21
+ # 5b. migrate methods
22
+ # 5c. render methods
23
+ # 6. text manipulation
24
+ # 7. command/option parser
25
+ # 8. executive method calls
26
+
27
+ # ===
11
28
  # Default settings
29
+ # ===
30
+
12
31
  @base_dir_def = Dir.pwd + '/'
13
32
  @base_dir = @base_dir_def
14
33
  @configs_dir = @base_dir + '_configs'
15
34
  @templates_dir = @base_dir + '_templates/'
16
35
  @data_dir = @base_dir + '_data/'
17
36
  @output_dir = @base_dir + '_output/'
18
- @config_file_def = @base_dir + '_configs/cfg-sample.yml'
19
- @config_file = @config_file_def
20
37
  @attributes_file_def = '_data/asciidoctor.yml'
21
38
  @attributes_file = @attributes_file_def
22
39
  @pdf_theme_file = 'theme/pdf-theme.yml'
@@ -31,73 +48,10 @@ require 'crack/xml'
31
48
  end
32
49
 
33
50
  # ===
34
- # General methods
51
+ # Executive methods
35
52
  # ===
36
53
 
37
- # Pull in a semi-structured data file, converting contents to a Ruby hash
38
- def get_data data
39
- # data must be a hash produced by data_hashify()
40
- if data['type']
41
- if data['type'].downcase == "yaml"
42
- data['type'] = "yml"
43
- end
44
- unless data['type'].downcase.match(/yml|json|xml|csv|regex/)
45
- @logger.error "Declared data type must be one of: yaml, json, xml, csv, or regex."
46
- raise "DataTypeUnrecognized"
47
- end
48
- else
49
- unless data['ext'].match(/\.yml|\.json|\.xml|\.csv/)
50
- @logger.error "Data file extension must be one of: .yml, .json, .xml, or .csv or else declared in config file."
51
- raise "FileExtensionUnknown (#{data[ext]})"
52
- end
53
- data['type'] = data['ext']
54
- data['type'].slice!(0) # removes leading dot char
55
- end
56
- case data['type']
57
- when "yml"
58
- begin
59
- return YAML.load_file(data['file'])
60
- rescue Exception => ex
61
- @logger.error "There was a problem with the data file. #{ex.message}"
62
- end
63
- when "json"
64
- begin
65
- return JSON.parse(File.read(data['file']))
66
- rescue Exception => ex
67
- @logger.error "There was a problem with the data file. #{ex.message}"
68
- end
69
- when "xml"
70
- begin
71
- data = Crack::XML.parse(File.read(data['file']))
72
- return data['root']
73
- rescue Exception => ex
74
- @logger.error "There was a problem with the data file. #{ex.message}"
75
- end
76
- when "csv"
77
- output = []
78
- i = 0
79
- begin
80
- CSV.foreach(data['file'], headers: true, skip_blanks: true) do |row|
81
- output[i] = row.to_hash
82
- i = i+1
83
- end
84
- output = {"data" => output}
85
- return output
86
- rescue
87
- @logger.error "The CSV format is invalid."
88
- end
89
- when "regex"
90
- if data['pattern']
91
- return parse_regex(data['file'], data['pattern'])
92
- else
93
- @logger.error "You must supply a regex pattern with your free-form data file."
94
- raise "MissingRegexPattern"
95
- end
96
- end
97
- end
98
-
99
54
  # Establish source, template, index, etc details for build jobs from a config file
100
- # TODO This needs to be turned into a Class?
101
55
  def config_build config_file
102
56
  @logger.debug "Using config file #{config_file}."
103
57
  validate_file_input(config_file, "config")
@@ -105,40 +59,38 @@ def config_build config_file
105
59
  config = YAML.load_file(config_file)
106
60
  rescue
107
61
  unless File.exists?(config_file)
108
- @logger.error "Config file not found."
62
+ @logger.error "Config file #{config_file} not found."
109
63
  else
110
- @logger.error "Problem loading config file. Exiting."
111
- end
112
- raise "Could not load #{config_file}"
113
- end
114
- validate_config_structure(config)
115
- if config['compile']
116
- for src in config['compile']
117
- data = src['data']
118
- for cfgn in src['builds']
119
- template = @base_dir + cfgn['template']
120
- unless cfgn['output'].downcase == "stdout"
121
- output = @base_dir + cfgn['output']
122
- else
123
- output = "stdout"
124
- end
125
- liquify(data, template, output)
126
- end
64
+ @logger.error "Problem loading config file #{config_file}. Exiting."
127
65
  end
66
+ raise "ConfigFileError"
128
67
  end
129
- if config['publish']
130
- begin
131
- for pub in config['publish']
132
- for bld in pub['builds']
133
- if bld['publish']
134
- publish(pub, bld)
135
- else
136
- @logger.warn "Publish build for '#{index}' backend '#{backend}' disabled."
137
- end
138
- end
68
+ cfg = BuildConfig.new(config) # convert the config file to a new object called 'cfg'
69
+ iterate_build(cfg)
70
+ end
71
+
72
+ def iterate_build cfg
73
+ stepcount = 0
74
+ for step in cfg.steps # iterate through each node in the 'config' object, which should start with an 'action' parameter
75
+ stepcount = stepcount + 1
76
+ step = BuildConfigStep.new(step) # create an instance of the Action class, validating the top-level step hash (now called 'step') in the process
77
+ type = step.type
78
+ case type # a switch to evaluate the 'action' parameter for each step in the iteration...
79
+ when "parse"
80
+ data = DataSrc.new(step.data)
81
+ builds = step.builds
82
+ for bld in builds
83
+ build = Build.new(bld, type) # create an instance of the Build class; Build.new accepts a 'bld' hash & action 'type'
84
+ liquify(data, build.template, build.output) # perform the liquify operation
139
85
  end
140
- rescue Exception => ex
141
- @logger.error "Error during publish action. #{ex}"
86
+ when "migrate"
87
+ @logger.warn "Migrate actions not yet implemented."
88
+ when "render"
89
+ @logger.warn "Render actions not yet implemented."
90
+ when "deploy"
91
+ @logger.warn "Deploy actions not yet implemented."
92
+ else
93
+ @logger.warn "The action `#{type}` is not valid."
142
94
  end
143
95
  end
144
96
  end
@@ -154,38 +106,258 @@ def validate_file_input file, type
154
106
  error = "The #{type} file (#{file}) was not found."
155
107
  end
156
108
  end
157
- unless error
158
- @logger.debug "Input file validated for #{type} file #{file}."
159
- else
109
+ if error
160
110
  @logger.error "Could not validate input file: #{error}"
161
111
  raise "InvalidInput"
162
112
  end
163
113
  end
164
114
 
165
115
  def validate_config_structure config
166
- unless config.is_a? Hash
167
- message = "The configuration file is not properly structured; it is not a hash"
116
+ unless config.is_a? Array
117
+ message = "The configuration file is not properly structured."
168
118
  @logger.error message
169
- raise message
119
+ raise "ConfigStructError"
170
120
  else
171
- unless config['publish'] or config['compile']
172
- raise "Config file must have at least one top-level section named 'publish:' or 'compile:'."
121
+ if (defined?(config['action'])).nil?
122
+ message = "Every listing in the configuration file needs an action type declaration."
123
+ @logger.error message
124
+ raise "ConfigStructError"
173
125
  end
174
126
  end
175
127
  # TODO More validation needed
176
128
  end
177
129
 
178
- def data_hashify data_var
179
- # TODO make datasource config a class
180
- if data_var.is_a?(String)
181
- data = {}
182
- data['file'] = data_var
183
- data['ext'] = File.extname(data_var)
184
- else # add ext to the hash
185
- data = data_var
186
- data['ext'] = File.extname(data['file'])
187
- end
188
- return data
130
+ # ===
131
+ # Core classes
132
+ # ===
133
+
134
+ # For now BuildConfig is mostly to objectify the primary build 'action' steps
135
+ class BuildConfig
136
+
137
+ def initialize config
138
+
139
+ if (defined?(config['compile'][0])) # The config is formatted for vesions < 0.3.0; convert it
140
+ config = deprecated_format(config)
141
+ end
142
+
143
+ # validations
144
+ unless config.is_a? Array
145
+ raise "ConfigStructError"
146
+ end
147
+
148
+ @@cfg = config
149
+ end
150
+
151
+ def steps
152
+ @@cfg
153
+ end
154
+
155
+ def deprecated_format config # for backward compatibility with 0.1.0 and 0.2.0
156
+ puts "You are using a deprecated configuration file structure. Update your config files; support for this structure will be dropped in version 1.0.0."
157
+ # There's only ever one item in the 'compile' array, and only one action type ("parse")
158
+ config['compile'].each do |n|
159
+ n.merge!("action" => "parse") # the action type was not previously declared
160
+ end
161
+ return config['compile']
162
+ end
163
+
164
+ end #class BuildConfig
165
+
166
+ class BuildConfigStep
167
+
168
+ def initialize step
169
+ @@step = step
170
+ @@logger = Logger.new(STDOUT)
171
+ if (defined?(@@step['action'])).nil?
172
+ @logger.error "Every step in the configuration file needs an 'action' type declared."
173
+ raise "ConfigStructError"
174
+ end
175
+ end
176
+
177
+ def type
178
+ return @@step['action']
179
+ end
180
+
181
+ def data
182
+ return @@step['data']
183
+ end
184
+
185
+ def builds
186
+ return @@step['builds']
187
+ end
188
+
189
+ def self.validate reqs
190
+ for req in reqs
191
+ if (defined?(@@step[req])).nil?
192
+ @@logger.error "Every #{@@step['action']}-type in the configuration file needs a '#{req}' declaration."
193
+ raise "ConfigStructError"
194
+ end
195
+ end
196
+ end
197
+
198
+ end #class Action
199
+
200
+ class Build
201
+
202
+ def initialize build, type
203
+ @@build = build
204
+ @@type = type
205
+ @@logger = Logger.new(STDOUT)
206
+ required = []
207
+ case type
208
+ when "parse"
209
+ required = ["template,output"]
210
+ when "render"
211
+ required = ["index,output"]
212
+ when "migrate"
213
+ required = ["source,target"]
214
+ end
215
+ for req in required
216
+ if (defined?(req)).nil?
217
+ raise ActionSettingMissing
218
+ end
219
+ end
220
+ end
221
+
222
+ def template
223
+ @@build['template']
224
+ end
225
+
226
+ def output
227
+ @@build['output']
228
+ end
229
+
230
+ def index
231
+ @@build['index']
232
+ end
233
+
234
+ def source
235
+ @@build['source']
236
+ end
237
+
238
+ def target
239
+ @@build['target']
240
+ end
241
+
242
+ end #class Build
243
+
244
+ class DataSrc
245
+ # initialization means establishing a proper hash for the 'data' param
246
+ def initialize datasrc
247
+ @@datasrc = {}
248
+ if datasrc.is_a? String # create a hash out of the filename
249
+ begin
250
+ @@datasrc['file'] = datasrc
251
+ @@datasrc['ext'] = File.extname(datasrc)
252
+ @@datasrc['type'] = false
253
+ @@datasrc['pattern'] = false
254
+ rescue
255
+ raise "InvalidDataFilename"
256
+ end
257
+ else
258
+ if datasrc.is_a? Hash # data var is a hash, so add 'ext' to it by extracting it from filename
259
+ @@datasrc['file'] = datasrc['file']
260
+ @@datasrc['ext'] = File.extname(datasrc['file'])
261
+ if (defined?(datasrc['pattern']))
262
+ @@datasrc['pattern'] = datasrc['pattern']
263
+ end
264
+ if (defined?(datasrc['type']))
265
+ @@datasrc['type'] = datasrc['type']
266
+ end
267
+ else # datasrc is neither String nor Hash
268
+ raise "InvalidDataSource"
269
+ end
270
+ end
271
+ end
272
+
273
+ def file
274
+ @@datasrc['file']
275
+ end
276
+
277
+ def ext
278
+ @@datasrc['ext']
279
+ end
280
+
281
+ def type
282
+ if @@datasrc['type'] # if we're carrying a 'type' setting for data, pass it along
283
+ datatype = @@datasrc['type']
284
+ if datatype.downcase == "yaml" # This is an expected common error, so let's do the user a solid
285
+ datatype = "yml"
286
+ end
287
+ else # If there's no 'type' defined, extract it from the filename and validate it
288
+ unless @@datasrc['ext'].downcase.match(/\.yml|\.json|\.xml|\.csv/)
289
+ # @logger.error "Data file extension must be one of: .yml, .json, .xml, or .csv or else declared in config file."
290
+ raise "FileExtensionUnknown"
291
+ end
292
+ datatype = @@datasrc['ext']
293
+ datatype = datatype[1..-1] # removes leading dot char
294
+ end
295
+ unless datatype.downcase.match(/yml|json|xml|csv|regex/) # 'type' must be one of these permitted vals
296
+ # @logger.error "Declared data type must be one of: yaml, json, xml, csv, or regex."
297
+ raise "DataTypeUnrecognized"
298
+ end
299
+ datatype
300
+ end
301
+
302
+ def pattern
303
+ @@datasrc['pattern']
304
+ end
305
+ end
306
+
307
+ # ===
308
+ # Action-specific methods
309
+ #
310
+ # PARSE-type build methods
311
+ # ===
312
+
313
+ # Pull in a semi-structured data file, converting contents to a Ruby hash
314
+ def ingest_data datasrc
315
+ # Must be passed a proper data object (there must be a better way to validate arg datatypes)
316
+ unless datasrc.is_a? Object
317
+ raise "InvalidDataObject"
318
+ end
319
+ # This method should really begin here, once the data object is in order
320
+ case datasrc.type
321
+ when "yml"
322
+ begin
323
+ return YAML.load_file(datasrc.file)
324
+ rescue Exception => ex
325
+ @logger.error "There was a problem with the data file. #{ex.message}"
326
+ end
327
+ when "json"
328
+ begin
329
+ return JSON.parse(File.read(datasrc.file))
330
+ rescue Exception => ex
331
+ @logger.error "There was a problem with the data file. #{ex.message}"
332
+ end
333
+ when "xml"
334
+ begin
335
+ data = Crack::XML.parse(File.read(datasrc.file))
336
+ return data['root']
337
+ rescue Exception => ex
338
+ @logger.error "There was a problem with the data file. #{ex.message}"
339
+ end
340
+ when "csv"
341
+ output = []
342
+ i = 0
343
+ begin
344
+ CSV.foreach(datasrc.file, headers: true, skip_blanks: true) do |row|
345
+ output[i] = row.to_hash
346
+ i = i+1
347
+ end
348
+ output = {"data" => output}
349
+ return output
350
+ rescue
351
+ @logger.error "The CSV format is invalid."
352
+ end
353
+ when "regex"
354
+ if datasrc.pattern
355
+ return parse_regex(datasrc.file, datasrc.pattern)
356
+ else
357
+ @logger.error "You must supply a regex pattern with your free-form data file."
358
+ raise "MissingRegexPattern"
359
+ end
360
+ end
189
361
  end
190
362
 
191
363
  def parse_regex data_file, pattern
@@ -214,17 +386,15 @@ def parse_regex data_file, pattern
214
386
  return output
215
387
  end
216
388
 
217
- # ===
218
- # Liquify BUILD methods
219
- # ===
220
-
221
- # Parse given data using given template, saving to given filename
222
- def liquify data, template_file, output
389
+ # Parse given data using given template, generating given output
390
+ def liquify datasrc, template_file, output
223
391
  @logger.debug "Executing liquify parsing operation."
224
- data = data_hashify(data)
225
- validate_file_input(data['file'], "data")
392
+ if datasrc.is_a? String
393
+ datasrc = DataSrc.new(datasrc)
394
+ end
395
+ validate_file_input(datasrc.file, "data")
226
396
  validate_file_input(template_file, "template")
227
- data = get_data(data) # gathers the data
397
+ data = ingest_data(datasrc)
228
398
  begin
229
399
  template = File.read(template_file) # reads the template file
230
400
  template = Liquid::Template.parse(template) # compiles template
@@ -253,6 +423,10 @@ def liquify data, template_file, output
253
423
  end
254
424
  end
255
425
 
426
+ # ===
427
+ # MIGRATE-type methods
428
+ # ===
429
+
256
430
  # Copy images and other assets into output dir for HTML operations
257
431
  def copy_assets src, dest
258
432
  if @recursive
@@ -273,7 +447,7 @@ def copy_assets src, dest
273
447
  end
274
448
 
275
449
  # ===
276
- # PUBLISH methods
450
+ # RENDER-type methods
277
451
  # ===
278
452
 
279
453
  # Gather attributes from a fixed attributes file
@@ -308,7 +482,7 @@ def publish pub, bld
308
482
  end
309
483
 
310
484
  # ===
311
- # Misc Classes, Modules, filters, etc
485
+ # Text manipulation Classes, Modules, filters, etc
312
486
  # ===
313
487
 
314
488
  class String
@@ -336,7 +510,7 @@ class String
336
510
 
337
511
  end
338
512
 
339
- # Liquid modules for text manipulation
513
+ # Extending Liquid filters/text manipulation
340
514
  module CustomFilters
341
515
  def plainwrap input
342
516
  input.wrap
@@ -382,11 +556,17 @@ module CustomFilters
382
556
 
383
557
  end
384
558
 
559
+ # register custom Liquid filters
385
560
  Liquid::Template.register_filter(CustomFilters)
386
561
 
562
+
563
+ # ===
564
+ # Command/options parser
565
+ # ===
566
+
387
567
  # Define command-line option/argument parameters
388
568
  # From the root directory of your project:
389
- # $ ./parse.rb --help
569
+ # $ liquidoc --help
390
570
  command_parser = OptionParser.new do|opts|
391
571
  opts.banner = "Usage: liquidoc [options]"
392
572
 
@@ -442,7 +622,6 @@ command_parser = OptionParser.new do|opts|
442
622
 
443
623
  end
444
624
 
445
- # Parse options.
446
625
  command_parser.parse!
447
626
 
448
627
  # Upfront debug output
@@ -450,10 +629,9 @@ command_parser.parse!
450
629
  @logger.debug "Config file: #{@config_file}"
451
630
  @logger.debug "Index file: #{@index_file}"
452
631
 
453
- # Parse data into docs!
454
- # liquify() takes the names of a Liquid template, a data file, and an output doc.
455
- # Input and output file extensions are non-determinant; your template
456
- # file establishes the structure.
632
+ # ===
633
+ # Execute
634
+ # ===
457
635
 
458
636
  unless @config_file
459
637
  if @data_file
@@ -1,3 +1,3 @@
1
1
  module Liquidoc
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
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.2.0
4
+ version: 0.3.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: 2017-10-18 00:00:00.000000000 Z
11
+ date: 2017-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler