vizbuilder 1.0.2 → 1.0.3

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 (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -9
  3. data/VERSION +1 -1
  4. data/lib/vizbuilder.rb +135 -86
  5. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 355a987affceba248fae74aa69124a43692fd4adf2e7b99c4630bcb23e171468
4
- data.tar.gz: 9fc566a7d1a66772a25120328fd9dd9c746c0fdeabf0d4df930b67231c42ec28
3
+ metadata.gz: e273e34c974ba38b41ede845375fde15653789fd4dfc5dd06b2c870b9b66852a
4
+ data.tar.gz: 261ebf230a9ef189707efeab09774837072058ab79aa0e16f854bcb145be62c5
5
5
  SHA512:
6
- metadata.gz: 4eeb652f8ebef7481efb3a817a66c3a859a0b3d98660c8753c68a8c9d5e1d22b1e72ac62eb4a2a49562305f8735657c876db106ee6270a88102ecb203294c300
7
- data.tar.gz: 64e29998acaa50cc9f8664618f569c4971c11f07b109ab2f02b3376c988c6f8320d9dd0e51c0d475a48c05da2f12e3c7cdf8b02ad574eacdaa639f94af4cba55
6
+ metadata.gz: 7a6503f0e5312e988b0636b54072ebf07de0dcd853a16aec190dd59a48d66ff8de6f0c5af814e83aa4b2560cdaef5e78d3ca50a2439391a18e9fe684db2da35e
7
+ data.tar.gz: 4e574ad770e138419ef08e0357ab1036d28c3a96f164213af3a0a29c1d7122b08fe340dc931f5541b77c7405d8216235f5e0a13d197d87f76d624c6cb7efe90c
data/README.md CHANGED
@@ -89,15 +89,7 @@ In `index.html.erb`:
89
89
 
90
90
  ## Configuration
91
91
 
92
- A new `VizBuilder` instance can be configured directly or via a block passed into the constructor:
93
-
94
- ```ruby
95
- app = VizBuilder.new
96
- app.set :some_global_config_thing, 'hello world'
97
- app.add_page 'index.html', template: 'index.html.erb'
98
- ```
99
-
100
- Or you can use a block:
92
+ A new `VizBuilder` instance is configured via a block passed into the constructor:
101
93
 
102
94
  ```ruby
103
95
  app = VizBuilder.new do
@@ -106,6 +98,14 @@ app = VizBuilder.new do
106
98
  end
107
99
  ```
108
100
 
101
+ You can add or adjust configuration directly on the `VizBuilder#config` instance, but
102
+ it may not work as expected:
103
+
104
+ ```ruby
105
+ app.config.set :some_global_config_thing, 'a different thing'
106
+ app.config.add_page 'index.html', template: 'index.html.erb'
107
+ ```
108
+
109
109
  There are a few config settings used by Viz Builder:
110
110
 
111
111
  #### http_prefix
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.2
1
+ 1.0.3
data/lib/vizbuilder.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'psych'
2
4
  require 'fileutils'
3
5
  require 'erb'
@@ -15,28 +17,24 @@ class VizBuilder
15
17
  attr_reader :config
16
18
  delegate :sitemap, :data, :hooks, :helper_modules, to: :config
17
19
 
18
- BUILD_DIR = 'build'.freeze
19
- PREBUILT_DIR = 'prebuild'.freeze
20
- DATA_DIR = 'data'.freeze
20
+ BUILD_DIR = 'build'
21
+ PREBUILT_DIR = 'prebuild'
22
+ DATA_DIR = 'data'
21
23
 
22
24
  def initialize(config = {}, &blk)
23
25
  # Config is an object used as the context for the given block
24
26
  @config = Config.new(config: config)
25
- # Load data into @data
26
- reload_data!
27
- # Execute the given block as a method of this class. The block can then use
28
- # the methods `add_page`, `add_data`, and `set`
29
- @config.instance_exec(&blk) if block_given?
30
- # Run any after load data hooks since they got added after the first load
31
- # data was called.
32
- run_hook!(:after_load_data)
33
- # Add helpers to the TemplateContext class
34
- TemplateContext.include(*helper_modules) unless helper_modules.empty?
27
+ @config_block = block_given? ? blk : nil
28
+ @config.helpers(Helpers)
29
+ @config.helpers(ConfigHelpers, type: :config)
30
+ @config.helpers(TemplateHelpers, type: :template)
31
+ @_configured = false
35
32
  end
36
33
 
37
34
  # Generate all pages in the sitemap and save to `build/`
38
35
  def build!(silent: false)
39
- ctx = TemplateContext.new(:production, :build, @config)
36
+ configure!(mode: :build, target: :production)
37
+ ctx = TemplateContext.new(@config)
40
38
  index_prebuilt!
41
39
  # First we build prebuilt pages that need digests calculated by build_page
42
40
  digested = sitemap.select { |_path, page| page[:digest] == true }
@@ -48,6 +46,7 @@ class VizBuilder
48
46
 
49
47
  # Run this builder as a server
50
48
  def runserver!(host: '127.0.0.1', port: '3456')
49
+ configure!(mode: :server, target: :development)
51
50
  status = 0 # running: 0, reload: 1, exit: 2
52
51
  # spawn a thread to watch the status flag and trigger a reload or exit
53
52
  monitor = Thread.new do
@@ -79,6 +78,8 @@ class VizBuilder
79
78
 
80
79
  # Support the call method so an instance can act as a Rack app
81
80
  def call(env)
81
+ raise 'VizBuilder configuration incomplete!' unless @_configured
82
+
82
83
  # Only support GET, OPTIONS, HEAD
83
84
  unless env['REQUEST_METHOD'].in?(%w[GET HEAD OPTIONS])
84
85
  return [405, { 'Content-Type' => 'text/plain' }, ['METHOD NOT ALLOWED']]
@@ -108,7 +109,7 @@ class VizBuilder
108
109
  # Check our sitemap then our prebuilt folder for content to serve
109
110
  if sitemap[path].present?
110
111
  content_type = MimeMagic.by_path(path).to_s
111
- ctx = TemplateContext.new(:development, :server, @config)
112
+ ctx = TemplateContext.new(@config)
112
113
  content = build_page(path, ctx)
113
114
  status = 200
114
115
  elsif File.exist?(build_filename)
@@ -126,6 +127,31 @@ class VizBuilder
126
127
  data.merge!(load_data)
127
128
  # TODO: after load hooks only run once, not every time reload_data! is called
128
129
  run_hook!(:after_load_data)
130
+ # Chainable
131
+ self
132
+ end
133
+
134
+ # Complete the configuration for this app. Is used by build! and runserver!.
135
+ # You will only need this method if you're running VizBuilder as a rack app
136
+ # without using runserver!.
137
+ def configure!(kwargs = nil)
138
+ return self if @_configured
139
+ # Update config with any kwargs
140
+ @config.merge!(kwargs) if kwargs.present?
141
+ # Load data into @data
142
+ reload_data!
143
+ # Execute the given block as a method of this class. The block can then use
144
+ # the methods `add_page`, `add_data`, and `set`
145
+ @config.instance_exec(&@config_block) if @config_block.present?
146
+ # Run any after load data hooks since they got added after the first load
147
+ # data was called.
148
+ run_hook!(:after_load_data)
149
+ # Add helpers to the TemplateContext class
150
+ TemplateContext.include(*helper_modules) unless helper_modules.empty?
151
+ # Mark app configured
152
+ @_configured = true
153
+ # Chainable
154
+ self
129
155
  end
130
156
 
131
157
  # Like File.extname, but gets all extensions if multiple are present
@@ -152,7 +178,7 @@ class VizBuilder
152
178
  # Convenience methods for configuring the site
153
179
  class Config
154
180
  attr_accessor :data, :config, :sitemap, :hooks, :helper_modules
155
- delegate :[], :[]=, :key?, to: :config
181
+ delegate :[], :[]=, :key?, :update, :merge!, to: :config
156
182
 
157
183
  def initialize(config: {})
158
184
  # Config is a Hash of site wide configuration variables
@@ -204,12 +230,12 @@ class VizBuilder
204
230
  end
205
231
 
206
232
  # Add helper modules or define helpers in a block
207
- def helpers(*mods, &blk)
233
+ def helpers(*mods, type: nil, &blk)
208
234
  new_helpers = []
209
235
  # loop over the list of arguments, making sure they're all modules, and
210
236
  # then add them to the list of new helpers
211
237
  mods.each do |mod|
212
- unless mods.is_a?(Module)
238
+ unless mod.is_a?(Module)
213
239
  raise ArgumentError, 'Helpers must be defined in a module or block'
214
240
  end
215
241
 
@@ -221,9 +247,9 @@ class VizBuilder
221
247
  if new_helpers.present?
222
248
  # extend the current Config instance with the helpers, making them available
223
249
  # to the rest of the configuration block
224
- extend(*new_helpers)
250
+ extend(*new_helpers) if type.nil? || type.to_sym == :config
225
251
  # add our new helpers to our array of all helpers
226
- @helper_modules += new_helpers
252
+ @helper_modules += new_helpers if type.nil? || type.to_sym == :template
227
253
  end
228
254
 
229
255
  self
@@ -248,11 +274,7 @@ class VizBuilder
248
274
  attr_accessor :page
249
275
  delegate :data, :sitemap, :config, to: :@config_obj
250
276
 
251
- def initialize(target, mode, config)
252
- # Target is development or production
253
- @target = target.to_sym
254
- # Mode is build or server
255
- @mode = mode.to_sym
277
+ def initialize(config)
256
278
  # Config is our Builder config object
257
279
  @config_obj = config
258
280
  # Page is a hash representing the page. Is the same as whats in sitemap
@@ -287,8 +309,49 @@ class VizBuilder
287
309
  end
288
310
  end
289
311
 
290
- # HELPERS
312
+ # Looks for an invoked method name in locals then in config, so locals
313
+ # and config vars can be used as if they're local vars in the template
314
+ def method_missing(sym)
315
+ return @locals[sym] if @locals.key?(sym)
316
+ return config[sym] if config.key?(sym)
317
+ super
318
+ end
319
+
320
+ def respond_to_missing?(sym, *)
321
+ @locals.key?(sym) || config.key?(sym) || super
322
+ end
323
+ end
324
+
325
+ # HELPERS
326
+
327
+ # The Helpers module holds methods that are added to both the config and
328
+ # template contexts. So you can use these in the config block and in
329
+ # templates.
330
+ module Helpers
331
+ # Are we running as a server?
332
+ def server?
333
+ config[:mode] == :server
334
+ end
335
+
336
+ # Are we building this app out?
337
+ def build?
338
+ config[:mode] == :build
339
+ end
340
+
341
+ # Is this in production?
342
+ def production?
343
+ config[:target] == :production
344
+ end
291
345
 
346
+ # Is this in development?
347
+ def development?
348
+ config[:target] == :development
349
+ end
350
+ end
351
+
352
+ # The TemplateHelpers modules hold methods that are added to the template
353
+ # context only. So you can only use these in templates.
354
+ module TemplateHelpers
292
355
  # Get the full URL to the root of this site
293
356
  def http_prefix
294
357
  return '/' if server? && development?
@@ -309,9 +372,12 @@ class VizBuilder
309
372
  def asset_path(*args)
310
373
  path = args.join('/')
311
374
  page = sitemap[path]
312
- if production? && page[:digest] == true
313
- raise "Missing digest for #{path}" if page[:digest_path].blank?
314
- path = page[:digest_path]
375
+ if production?
376
+ raise "Missing asset #{path}" if page.blank?
377
+ if page[:digest] == true
378
+ raise "Missing digest for #{path}" if page[:digest_path].blank?
379
+ path = page[:digest_path]
380
+ end
315
381
  end
316
382
  asset_http_prefix + path
317
383
  end
@@ -320,40 +386,12 @@ class VizBuilder
320
386
  def canonical_url(*args)
321
387
  http_prefix + args.join('/')
322
388
  end
323
-
324
- # Are we running as a server?
325
- def server?
326
- @mode == :server
327
- end
328
-
329
- # Are we building this app out?
330
- def build?
331
- @mode == :build
332
- end
333
-
334
- # Is this in production?
335
- def production?
336
- @target == :production
337
- end
338
-
339
- # Is this in development?
340
- def development?
341
- @target == :development
342
- end
343
-
344
- # Looks for an invoked method name in locals then in config, so locals
345
- # and config vars can be used as if they're local vars in the template
346
- def method_missing(sym)
347
- return @locals[sym] if @locals.key?(sym)
348
- return config[sym] if config.key?(sym)
349
- super
350
- end
351
-
352
- def respond_to_missing?(sym, *)
353
- @locals.key?(sym) || config.key?(sym) || super
354
- end
355
389
  end
356
390
 
391
+ # The ConfigHelpers modules hold methods that are added to the config block
392
+ # context only. So you can only use these with the config object instance.
393
+ module ConfigHelpers; end
394
+
357
395
  private
358
396
 
359
397
  # Generate one page from the sitemap and save to `build/`
@@ -362,25 +400,35 @@ class VizBuilder
362
400
  out_fname = File.join(BUILD_DIR, path)
363
401
  puts "Rendering #{out_fname}..." unless silent
364
402
 
403
+ # Check if we have a layout defined, use it
404
+ layout = ctx.page['layout'] || config['layout'] || nil
405
+
365
406
  # Check page data for info on how to build this path
366
407
  if ctx.page['template'].present?
367
- content = ctx.render(ctx.page['template'])
368
- elsif ctx.page['json'].present?
369
- content = ctx.page['json'].to_json
370
- elsif ctx.page['yaml'].present?
371
- content = ctx.page['yaml'].to_yaml
372
- elsif ctx.page['file'].present?
373
- content = File.read(ctx.page['file'])
408
+ # Make sure to render the template inside the layout render so code in the
409
+ # erb layout and template are executed in a sensible order.
410
+ content = \
411
+ if layout.present?
412
+ ctx.render(layout) { ctx.render(ctx.page['template']) }
413
+ else
414
+ ctx.render(ctx.page['template'])
415
+ end
374
416
  else
375
- raise(
376
- ArgumentError,
377
- "Page '#{path}' missing one of required attributes: 'template', 'json', 'yaml', 'file'."
378
- )
379
- end
417
+ # Everything else static data and not executable, so we don't have to load
418
+ # the content inside the layout execution.
419
+ if ctx.page['json'].present?
420
+ content = ctx.page['json'].to_json
421
+ elsif ctx.page['file'].present?
422
+ content = File.read(ctx.page['file'])
423
+ else
424
+ raise(
425
+ ArgumentError,
426
+ "Page '#{path}' missing one of required attributes: 'template', 'json', 'file'."
427
+ )
428
+ end
380
429
 
381
- # Check if we have a layout defined, use it
382
- layout_fname = ctx.page['layout'] || config['layout']
383
- content = ctx.render(layout_fname) { content } if layout_fname.present?
430
+ content = ctx.render(layout) { content } if layout.present?
431
+ end
384
432
 
385
433
  # If page data includes a digest flag, add sha1 digest to output filename
386
434
  if ctx.page['digest'] == true
@@ -403,16 +451,17 @@ class VizBuilder
403
451
  def load_data
404
452
  data = {}.with_indifferent_access
405
453
 
406
- Dir.glob("#{DATA_DIR}/*.json") do |fname|
407
- key = File.basename(fname, '.json').to_sym
408
- puts "Loading data[:#{key}] from #{fname}..."
409
- data[key] = JSON.parse(File.read(fname))
410
- end
411
-
412
- Dir.glob("#{DATA_DIR}/*.yaml") do |fname|
413
- key = File.basename(fname, '.yaml').to_sym
414
- puts "Loading data[:#{key}] from #{fname}..."
415
- data[key] = Psych.parse(fname)
454
+ %w[.json .yaml].each do |ext|
455
+ Dir.glob("#{DATA_DIR}/*#{ext}") do |fname|
456
+ key = File.basename(fname, ext).to_sym
457
+ puts "Loading data[:#{key}] from #{fname}..."
458
+ data[key] = \
459
+ if ext == '.json'
460
+ JSON.parse(File.read(fname))
461
+ else
462
+ Psych.parse(fname)
463
+ end
464
+ end
416
465
  end
417
466
 
418
467
  data
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vizbuilder
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Mark
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-30 00:00:00.000000000 Z
11
+ date: 2019-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -146,7 +146,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
146
  - !ruby/object:Gem::Version
147
147
  version: '0'
148
148
  requirements: []
149
- rubygems_version: 3.0.1
149
+ rubyforge_project:
150
+ rubygems_version: 2.7.6.2
150
151
  signing_key:
151
152
  specification_version: 4
152
153
  summary: Simple and fast static site generator