vizbuilder 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rubocop.yml +53 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +7 -0
- data/LICENSE +27 -0
- data/README.md +331 -0
- data/Rakefile +10 -0
- data/VERSION +1 -0
- data/do_release.sh +35 -0
- data/lib/viz-builder.rb +1 -0
- data/lib/viz_builder.rb +1 -0
- data/lib/vizbuilder.rb +420 -0
- data/vizbuilder.gemspec +26 -0
- metadata +153 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 51f92c1fd3944056565422dd247601a8b15ba9e5e173edd596689b1511469e39
|
4
|
+
data.tar.gz: '029ed027b6ca7620051c6a3003f6d4dcb646faf9cb7699af5dda3bddf56be122'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4f46a7be4d80cf2f2cc1b4b77d667ace49f540896397b0509aaca1149df7a0ef711b56b7d51dc26f9d390d33f69138d215759f860417c3671c261024383f4aeb
|
7
|
+
data.tar.gz: a9af4e089431d541b393a2b881d7a446d03a6597b858f0cf2c59d3d9b380a80d85b1c968c8c23006801fe74c08a25f6a7eda7372ec3dd7d8208763a7c5ac876e
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
AllCops:
|
2
|
+
Include:
|
3
|
+
- '**/Rakefile'
|
4
|
+
- '*gemspec'
|
5
|
+
- '**/*.rb'
|
6
|
+
|
7
|
+
Style/GuardClause:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Style/FileName:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Style/LineLength:
|
14
|
+
Max: 100
|
15
|
+
|
16
|
+
Style/NumericLiterals:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Style/ClassAndModuleChildren:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Style/SignalException:
|
23
|
+
EnforcedStyle: only_raise
|
24
|
+
|
25
|
+
Style/ClassLength:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Style/MethodLength:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
Style/GuardClause:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
Style/IfUnlessModifier:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
Metrics/CyclomaticComplexity:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Style/Alias:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
Metrics/BlockLength:
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
Metrics/AbcSize:
|
47
|
+
Enabled: false
|
48
|
+
|
49
|
+
Metrics/PerceivedComplexity:
|
50
|
+
Enabled: false
|
51
|
+
|
52
|
+
Layout/EmptyLineAfterGuardClause:
|
53
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
File without changes
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2019, Vox Media, Inc., Ryan Mark
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
* Neither the name of the {organization} nor the names of its
|
15
|
+
contributors may be used to endorse or promote products derived from
|
16
|
+
this software without specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,331 @@
|
|
1
|
+
A fast, simple static site builder, inspired by [Middleman](https://middlemanapp.com/). Built for news.
|
2
|
+
|
3
|
+
## Why another static site generator?
|
4
|
+
|
5
|
+
We've been using Middleman in Vox Media newsrooms for many years to build static articles and embeddable interactives. But as we've built larger projects and projects that require frequent updates, we've found that Middleman's extensive dependencies and complexity can cause high resource use and long build times. This is not a criticism of Middleman, Middleman is great and you should use it if it does what you need.
|
6
|
+
|
7
|
+
We need something simple and fast. Something that provides only the functionality we need and uses a few simple dependencies.
|
8
|
+
|
9
|
+
This is a simple ruby library and doesn't include any setup or project creation tools. If you want a helpful setup wizard or an ecosystem of extensions and tooling, checkout Middleman or a different static site generator.
|
10
|
+
|
11
|
+
## Quick Start
|
12
|
+
|
13
|
+
Viz Builder is a simple library that leaves it to you to load and invoke it. There is no shell command for running it or setting up a new project.
|
14
|
+
|
15
|
+
To your project's `Gemfile`, add:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'vizbuilder'
|
19
|
+
```
|
20
|
+
|
21
|
+
Then in a new or existing file, you will `require` it, configure it and either run as a development server or to create static html and assets ready for upload.
|
22
|
+
|
23
|
+
Here is an example file `app.rb` that will load, configure and run Viz Builder:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require 'vizbuilder'
|
27
|
+
|
28
|
+
app = VizBuilder.new do
|
29
|
+
set :some_global_config_thing, 'hello world'
|
30
|
+
add_page 'index.html', template: 'index.html.erb'
|
31
|
+
end
|
32
|
+
|
33
|
+
# if run with the argument `runserver` run as a development server
|
34
|
+
if ARGV[0] == 'runserver'
|
35
|
+
app.runserver
|
36
|
+
else
|
37
|
+
app.build
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
And then execute your app:
|
42
|
+
|
43
|
+
```
|
44
|
+
$ bundle exec ruby app.rb runserver
|
45
|
+
```
|
46
|
+
|
47
|
+
## Data
|
48
|
+
|
49
|
+
Viz Builder manages configuration, sitemap and project data in three hashes that you can access directly in templates or the configuration block.
|
50
|
+
|
51
|
+
Configuration variables added via the `set` method and can be accessed in the `config` Hash:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
set :foo, 'bar'
|
55
|
+
config[:foo] == 'bar'
|
56
|
+
```
|
57
|
+
|
58
|
+
Data variables can be added via the `add_data` method and can be accessed in the `data` Hash:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
add_data :scraped, foo: 'bar'
|
62
|
+
data[:scraped] == { foo: 'bar' }
|
63
|
+
# data[:scraped][:foo]
|
64
|
+
# or
|
65
|
+
# data.dig(:scraped, :foo)
|
66
|
+
```
|
67
|
+
|
68
|
+
Viz Builder automatically loads all JSON and YAML files located in the `data` directory. The base file name will be used as the name:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
# assuming a file called `data/my_content.json`
|
72
|
+
data[:my_content] == JSON.parse(File.read('data/my_content.json'))
|
73
|
+
```
|
74
|
+
|
75
|
+
Viz Builder keeps track of pages in a `sitemap` Hash. Pages are added with `add_page`:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
add_page 'index.html', template: 'index.html.erb', foo: 'bar'
|
79
|
+
# sitemap['index.html'] == { template: 'index.html.erb', foo: 'bar' }
|
80
|
+
```
|
81
|
+
|
82
|
+
In the context of the template for this page, the data given to `add_page` is accessible in the `page` hash.
|
83
|
+
|
84
|
+
In `index.html.erb`:
|
85
|
+
```ERB
|
86
|
+
<% page == { template: 'index.html.erb', foo: 'bar' } %>
|
87
|
+
<%=page[:foo] # 'bar' %>
|
88
|
+
```
|
89
|
+
|
90
|
+
## Configuration
|
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:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
app = VizBuilder.new do
|
104
|
+
set :some_global_config_thing, 'hello world'
|
105
|
+
add_page 'index.html', template: 'index.html.erb'
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
There are a few config settings used by Viz Builder:
|
110
|
+
|
111
|
+
#### http_prefix
|
112
|
+
|
113
|
+
Used during building for setting canonical urls and links between pages of the site. Used by the `canonical_url` helper detailed below.
|
114
|
+
|
115
|
+
#### asset_http_prefix
|
116
|
+
|
117
|
+
Used during building for setting asset urls. Used by the `asset_path` helper detailed below.
|
118
|
+
|
119
|
+
#### layout
|
120
|
+
|
121
|
+
An optional setting that will render all pages inside the specified ERB layout file.
|
122
|
+
|
123
|
+
## Templates
|
124
|
+
|
125
|
+
Templates are loaded by path from the root of your project directory. There are currently no special locations from which template files are loaded. If you keep your templates in a subdirectory of your project, you'll need to include the directory when specifying the file:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
# keeping our templates in a subdirectory called 'templates'
|
129
|
+
app = VizBuilder.new do
|
130
|
+
add_page 'index.html', template: 'templates/index.html.erb'
|
131
|
+
add_page 'article.html', template: 'templates/article.html.erb'
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
#### Layouts
|
136
|
+
|
137
|
+
You can have your templates render inside a `layout` file by either setting a global `:layout` or by setting the layout on a page-by-page basis.
|
138
|
+
|
139
|
+
To render all pages in the app in a layout:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
app = VizBuilder.new do
|
143
|
+
set :layout, 'layout.html.erb'
|
144
|
+
add_page 'index.html', template: 'index.html.erb'
|
145
|
+
add_page 'article.html', template: 'article.html.erb'
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
If you don't want a layout used for all pages, or if one page needs to use a different layout, you can pass `layout` to `add_page`:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
app = VizBuilder.new do
|
153
|
+
add_page 'index.html', template: 'index.html.erb', layout: 'layout.html.erb'
|
154
|
+
add_page 'article.html', template: 'article.html.erb', layout: 'article_layout.html.erb'
|
155
|
+
add_page 'snippet.html', template: 'snippet.html.erb' # no layout is used for this one
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
## Helpers
|
160
|
+
|
161
|
+
There are a few helpers included in Viz Builder, and you can add your own as well. Helpers are methods that are usable from a template or from the configuration block.
|
162
|
+
|
163
|
+
To add helpers:
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
app = VizBuilder.new do
|
167
|
+
...
|
168
|
+
|
169
|
+
helpers do
|
170
|
+
def do_something(args)
|
171
|
+
# do stuff
|
172
|
+
'stuff is done'
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
You can also break your helpers out into a separate file and module.
|
179
|
+
|
180
|
+
So in a new file called `helpers.rb` we add:
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
module MyHelpers
|
184
|
+
def do_something(args)
|
185
|
+
# do stuff
|
186
|
+
'stuff is done'
|
187
|
+
end
|
188
|
+
end
|
189
|
+
```
|
190
|
+
|
191
|
+
Then in your `app.rb` add:
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
require 'helpers.rb'
|
195
|
+
|
196
|
+
app = VizBuilder.new do
|
197
|
+
...
|
198
|
+
|
199
|
+
helpers MyHelpers
|
200
|
+
end
|
201
|
+
```
|
202
|
+
|
203
|
+
Helpers can be used immediately in the configuration block in addition to in the template:
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
app = VizBuilder.new do
|
207
|
+
...
|
208
|
+
|
209
|
+
helpers do
|
210
|
+
def do_something(args)
|
211
|
+
# do stuff
|
212
|
+
'stuff is done'
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
do_something :stuff
|
217
|
+
end
|
218
|
+
```
|
219
|
+
|
220
|
+
WARNING: You should probably not use instance variables in helpers (`@myvariable`), because the instance variables are not shared between the configuration block and templates. You can use `set` and `add_data` to stash data between helper uses.
|
221
|
+
|
222
|
+
### Built-in helpers
|
223
|
+
|
224
|
+
#### render
|
225
|
+
|
226
|
+
Render a template or partial and return the output. Takes the path to the template and a hash of local variables.
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
<%=render 'list.html.erb', list_items: items %>
|
230
|
+
```
|
231
|
+
|
232
|
+
#### include_file
|
233
|
+
|
234
|
+
Load and return the contents of a file. Good for inlining CSS or SVG content.
|
235
|
+
|
236
|
+
```erb
|
237
|
+
<%=include_file 'logo.svg' %>
|
238
|
+
```
|
239
|
+
|
240
|
+
#### http_prefix
|
241
|
+
|
242
|
+
Returns the value of the config item `http_prefix`. Should represent the root path to the site.
|
243
|
+
|
244
|
+
#### asset_http_prefix
|
245
|
+
|
246
|
+
Returns the value of the config item `asset_http_prefix` or `http_prefix`. Should represent the root path to all assets for the site.
|
247
|
+
|
248
|
+
#### asset_path
|
249
|
+
|
250
|
+
Returns a working url pointing to a given asset. Any provided arguments will be added to the returned url:
|
251
|
+
|
252
|
+
```erb
|
253
|
+
<%=asset_path :images, 'logo.png' %>
|
254
|
+
<!-- outputs {asset_http_prefix}/images/logo.png --->
|
255
|
+
<%=asset_path 'images/logo.png' %>
|
256
|
+
<!-- also outputs {asset_http_prefix}/images/logo.png --->
|
257
|
+
```
|
258
|
+
|
259
|
+
#### canonical_url
|
260
|
+
|
261
|
+
Returns the full domain name and path to the root of the site. Any provided arguments will be added to the returned url:
|
262
|
+
|
263
|
+
```erb
|
264
|
+
<%=canonical_url 'page1' %>
|
265
|
+
<!-- outputs {http_prefix}/page1 --->
|
266
|
+
```
|
267
|
+
|
268
|
+
#### build?
|
269
|
+
|
270
|
+
True if Viz Builder is building out all the pages and not running as a rack app (aka web server).
|
271
|
+
|
272
|
+
#### server?
|
273
|
+
|
274
|
+
True if Viz Builder is running as a rack app (aka web server).
|
275
|
+
|
276
|
+
#### development?
|
277
|
+
|
278
|
+
True if Viz Builder is running in development mode. Usually synonymous with `server?`.
|
279
|
+
|
280
|
+
#### production?
|
281
|
+
|
282
|
+
True if Viz Builder is running in production mode. Usually synonymous with `build?`.
|
283
|
+
|
284
|
+
## Assets
|
285
|
+
|
286
|
+
You can generate Javascript and CSS using the same template processing used for HTML files. Just add a JS or CSS file as a page in your app configuration:
|
287
|
+
|
288
|
+
```ruby
|
289
|
+
app = VizBuilder.new do
|
290
|
+
add_page 'index.html', template: 'index.html.erb'
|
291
|
+
add_page 'app.js', template: 'app.js.erb'
|
292
|
+
add_page 'app.css', template: 'app.css.erb'
|
293
|
+
end
|
294
|
+
```
|
295
|
+
|
296
|
+
Viz Builder will also look in the `prebuild` directory for static assets. The builder will copy these files into the `build` directory during the build process, or it will serve these files directly during development server.
|
297
|
+
|
298
|
+
Viz Builder will not transform or minify assets for you. It will not handle SCSS and it will not compile ES6+ down to ES5 Javascript. I recommend using something like [webpack](https://webpack.js.org/) to handle processing javascript and stylesheets. Whatever tool you use should be configured to save output files to `prebuild`.
|
299
|
+
|
300
|
+
If you only have a little script and style in your site, we recommend just using vanilla Javascript and CSS.
|
301
|
+
|
302
|
+
#### Asset hashing
|
303
|
+
|
304
|
+
Viz Builder automatically adds sha1 crypto hashes to the filenames of certain asset files when building out a site. Any files from the `prebuild` directory will get these hashes. You can have these crypto hashes added to any page with the `digest` option:
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
app = VizBuilder.new do
|
308
|
+
add_page 'app.js', template: 'app.js.erb', digest: true
|
309
|
+
end
|
310
|
+
```
|
311
|
+
|
312
|
+
WARNING: You probably don't want to use the `digest` on HTML pages.
|
313
|
+
|
314
|
+
You must use the `asset_path` helper to always get a proper URL for your asset files:
|
315
|
+
|
316
|
+
```ERB
|
317
|
+
<script src="<%=asset_path 'javascript/app.js' %>"></script>
|
318
|
+
<link ref="stylesheet" href="<%=asset_path 'stylesheets/app.css' %>" />
|
319
|
+
```
|
320
|
+
|
321
|
+
WARNING: If you do not use the `asset_path` helper, stuff will work in development but will break when you deploy.
|
322
|
+
|
323
|
+
## Contributing
|
324
|
+
|
325
|
+
Fork this repo, create a new branch on your fork, and make your changes there.
|
326
|
+
Open a pull request on this repo for consideration.
|
327
|
+
|
328
|
+
If its a small bugfix, feel free making the changes and opening a PR. If it's a
|
329
|
+
feature addition or a more substantial change, please open a github issue
|
330
|
+
outlining the feature or change. This is just to save you time and make sure
|
331
|
+
your efforts can get aligned with other folks' plans.
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/do_release.sh
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
if ! git diff-index --quiet HEAD --; then
|
4
|
+
echo You must commit all your changes before updating the version
|
5
|
+
exit 1
|
6
|
+
fi
|
7
|
+
|
8
|
+
old_version=$(cat VERSION)
|
9
|
+
|
10
|
+
if [ $# -ne 1 ]; then
|
11
|
+
read -p "Current version is $old_version. Enter a new version: " version
|
12
|
+
else
|
13
|
+
version=$1
|
14
|
+
fi
|
15
|
+
|
16
|
+
if [ "$old_version" = "$version" ]; then
|
17
|
+
echo Already at version $version
|
18
|
+
exit 1
|
19
|
+
fi
|
20
|
+
|
21
|
+
echo Updating version to $version
|
22
|
+
|
23
|
+
echo $version > VERSION
|
24
|
+
|
25
|
+
read -p "Do you wish to commit the new version, tag and push? [y/N] " tyn
|
26
|
+
if echo "$tyn" | grep -iq "^y"; then
|
27
|
+
git commit -am "bump to $version" && git tag v$version && git push && git push --tags
|
28
|
+
|
29
|
+
read -p "Do you wish to build and publish the release? [y/N] " pyn
|
30
|
+
if echo "$pyn" | grep -iq "^y"; then
|
31
|
+
rm *.gem
|
32
|
+
gem build vizbuilder.gemspec
|
33
|
+
gem push *.gem
|
34
|
+
fi
|
35
|
+
fi
|
data/lib/viz-builder.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'vizbuilder'
|
data/lib/viz_builder.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'vizbuilder'
|
data/lib/vizbuilder.rb
ADDED
@@ -0,0 +1,420 @@
|
|
1
|
+
require 'psych'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'erb'
|
4
|
+
require 'json'
|
5
|
+
require 'rack'
|
6
|
+
require 'mimemagic'
|
7
|
+
require 'active_support/all'
|
8
|
+
require 'ruby_dig'
|
9
|
+
require 'base64'
|
10
|
+
require 'digest'
|
11
|
+
require 'english'
|
12
|
+
|
13
|
+
# The VizBuilder class does all the stuff for a viz builder app
|
14
|
+
class VizBuilder
|
15
|
+
attr_reader :config
|
16
|
+
delegate :sitemap, :data, :hooks, :helper_modules, to: :config
|
17
|
+
|
18
|
+
BUILD_DIR = 'build'.freeze
|
19
|
+
PREBUILT_DIR = 'prebuild'.freeze
|
20
|
+
DATA_DIR = 'data'.freeze
|
21
|
+
|
22
|
+
def initialize(config = {}, &blk)
|
23
|
+
# Config is an object used as the context for the given block
|
24
|
+
@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?
|
35
|
+
end
|
36
|
+
|
37
|
+
# Generate all pages in the sitemap and save to `build/`
|
38
|
+
def build!(silent: false)
|
39
|
+
ctx = TemplateContext.new(:production, :build, @config)
|
40
|
+
index_prebuilt!
|
41
|
+
# First we build prebuilt pages that need digests calculated by build_page
|
42
|
+
digested = sitemap.select { |_path, page| page[:digest] == true }
|
43
|
+
digested.each { |path, _page| build_page(path, ctx, silent: silent) }
|
44
|
+
# Then we build all other pages
|
45
|
+
undigested = sitemap.reject { |_path, page| page[:digest] == true }
|
46
|
+
undigested.each { |path, _page| build_page(path, ctx, silent: silent) }
|
47
|
+
end
|
48
|
+
|
49
|
+
# Run this builder as a server
|
50
|
+
def runserver!(host: '127.0.0.1', port: '3456')
|
51
|
+
status = 0 # running: 0, reload: 1, exit: 2
|
52
|
+
# spawn a thread to watch the status flag and trigger a reload or exit
|
53
|
+
monitor = Thread.new do
|
54
|
+
sleep 0.1 while status.zero?
|
55
|
+
# Shutdown the server, wait for it to finish and then wait a tick
|
56
|
+
Rack::Handler::WEBrick.shutdown
|
57
|
+
sleep 0.1
|
58
|
+
# Use ps to get the command that the user executed, and use Kernel.exec
|
59
|
+
# to execute the command, replacing the current process.
|
60
|
+
# Basically restart everything.
|
61
|
+
Kernel.exec(`ps #{$PID} -o command`.split("\n").last) if status == 1
|
62
|
+
end
|
63
|
+
|
64
|
+
# trap ctrl-c and set status flag
|
65
|
+
trap('SIGINT') do
|
66
|
+
if status == 1
|
67
|
+
status = 2 # ctrl-c hit twice or more, set status to exit
|
68
|
+
elsif status.zero?
|
69
|
+
# ctrl-c hit once, notify user and set status to reload
|
70
|
+
puts "\nReloading the server, hit ctrl-c twice to exit\n"
|
71
|
+
status = 1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
puts "\nStarting Dev server, hit ctrl-c once to reload, twice to exit\n"
|
76
|
+
Rack::Handler::WEBrick.run(self, Host: host, Port: port)
|
77
|
+
monitor.join # let the monitor thread finish its work
|
78
|
+
end
|
79
|
+
|
80
|
+
# Support the call method so an instance can act as a Rack app
|
81
|
+
def call(env)
|
82
|
+
# Only support GET, OPTIONS, HEAD
|
83
|
+
unless env['REQUEST_METHOD'].in?(%w[GET HEAD OPTIONS])
|
84
|
+
return [405, { 'Content-Type' => 'text/plain' }, ['METHOD NOT ALLOWED']]
|
85
|
+
end
|
86
|
+
|
87
|
+
# default response is 404 not found
|
88
|
+
status = 404
|
89
|
+
content_type = 'text/plain'
|
90
|
+
content = '404 File not found'
|
91
|
+
|
92
|
+
# Validate the requested path
|
93
|
+
path = env['PATH_INFO']
|
94
|
+
if path =~ %r{/$}
|
95
|
+
# path is for a directory
|
96
|
+
path += 'index.html'
|
97
|
+
elsif path =~ %r{/[^./]+$}
|
98
|
+
# path looks like a directory but is missing a trailing slash
|
99
|
+
path += '/index.html'
|
100
|
+
end
|
101
|
+
|
102
|
+
# remove any leading slashes
|
103
|
+
path = path[1..-1] if path =~ %r{^/}
|
104
|
+
|
105
|
+
# filename for a potential prebuilt asset
|
106
|
+
build_filename = File.join(PREBUILT_DIR, path)
|
107
|
+
|
108
|
+
# Check our sitemap then our prebuilt folder for content to serve
|
109
|
+
if sitemap[path].present?
|
110
|
+
content_type = MimeMagic.by_path(path).to_s
|
111
|
+
ctx = TemplateContext.new(:development, :server, @config)
|
112
|
+
content = build_page(path, ctx)
|
113
|
+
status = 200
|
114
|
+
elsif File.exist?(build_filename)
|
115
|
+
content_type = MimeMagic.by_path(path).to_s
|
116
|
+
content = File.read(build_filename)
|
117
|
+
status = 200
|
118
|
+
end
|
119
|
+
|
120
|
+
# Status code, headers and content for this response
|
121
|
+
[status, { 'Content-Type' => content_type }, [content]]
|
122
|
+
end
|
123
|
+
|
124
|
+
# Force a reload of data files in `data/`
|
125
|
+
def reload_data!
|
126
|
+
data.merge!(load_data)
|
127
|
+
# TODO: after load hooks only run once, not every time reload_data! is called
|
128
|
+
run_hook!(:after_load_data)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Like File.extname, but gets all extensions if multiple are present
|
132
|
+
def self.fullextname(path)
|
133
|
+
fname = File.basename(path)
|
134
|
+
parts = fname.split('.')
|
135
|
+
".#{parts[1..-1].join('.')}"
|
136
|
+
end
|
137
|
+
|
138
|
+
# Convenience methods for configuring the site
|
139
|
+
class Config
|
140
|
+
attr_accessor :data, :config, :sitemap, :hooks, :helper_modules
|
141
|
+
delegate :[], :[]=, :key?, to: :config
|
142
|
+
|
143
|
+
def initialize(config: {})
|
144
|
+
# Config is a Hash of site wide configuration variables
|
145
|
+
@config = config.with_indifferent_access
|
146
|
+
# Data contains content and data that needs displaying
|
147
|
+
@data = {}.with_indifferent_access
|
148
|
+
# Hooks is a hash of hook names and arrays of blocks registered to be
|
149
|
+
# executed when those hooks are reached
|
150
|
+
@hooks = {}.with_indifferent_access
|
151
|
+
# Sitemap is a hash representing the documents in the site that will be
|
152
|
+
# processed by Builder. No indifferent access since the keys are all
|
153
|
+
# file paths.
|
154
|
+
@sitemap = {}
|
155
|
+
# Helpers is an array of Modules that will get mixed into the template
|
156
|
+
# context and with the current Config instance
|
157
|
+
@helper_modules = []
|
158
|
+
end
|
159
|
+
|
160
|
+
# Add a page to the sitemap
|
161
|
+
def add_page(path, kwargs)
|
162
|
+
@sitemap[path] = kwargs.with_indifferent_access
|
163
|
+
self
|
164
|
+
end
|
165
|
+
|
166
|
+
# Add or replace data
|
167
|
+
def add_data(key, val)
|
168
|
+
@data[key] = val.is_a?(Hash) ? val.with_indifferent_access : val
|
169
|
+
self
|
170
|
+
end
|
171
|
+
|
172
|
+
# Set a global config option
|
173
|
+
def set(key, val)
|
174
|
+
@config[key] = val.is_a?(Hash) ? val.with_indifferent_access : val
|
175
|
+
self
|
176
|
+
end
|
177
|
+
|
178
|
+
# Add code to run after data is loaded or reloaded. Is also run after object
|
179
|
+
# is created.
|
180
|
+
def after_load_data(&blk)
|
181
|
+
hook(:after_load_data, &blk)
|
182
|
+
self
|
183
|
+
end
|
184
|
+
|
185
|
+
# Add a block to a hook name
|
186
|
+
def hook(name, &blk)
|
187
|
+
@hooks[name.to_sym] ||= []
|
188
|
+
@hooks[name.to_sym] << blk
|
189
|
+
self
|
190
|
+
end
|
191
|
+
|
192
|
+
# Add helper modules or define helpers in a block
|
193
|
+
def helpers(*mods, &blk)
|
194
|
+
new_helpers = []
|
195
|
+
# loop over the list of arguments, making sure they're all modules, and
|
196
|
+
# then add them to the list of new helpers
|
197
|
+
mods.each do |mod|
|
198
|
+
unless mods.is_a?(Module)
|
199
|
+
raise ArgumentError, 'Helpers must be defined in a module or block'
|
200
|
+
end
|
201
|
+
|
202
|
+
new_helpers << mod
|
203
|
+
end
|
204
|
+
# if block is given, turn it into a module and add it to the helpers list
|
205
|
+
new_helpers << Module.new(&blk) if blk
|
206
|
+
|
207
|
+
if new_helpers.present?
|
208
|
+
# extend the current Config instance with the helpers, making them available
|
209
|
+
# to the rest of the configuration block
|
210
|
+
extend(*new_helpers)
|
211
|
+
# add our new helpers to our array of all helpers
|
212
|
+
@helper_modules += new_helpers
|
213
|
+
end
|
214
|
+
|
215
|
+
self
|
216
|
+
end
|
217
|
+
|
218
|
+
def respond_to_missing?(sym, *)
|
219
|
+
config.key?(sym) || super
|
220
|
+
end
|
221
|
+
|
222
|
+
# Treat config options as local
|
223
|
+
def method_missing(sym)
|
224
|
+
return config[sym] if config.key?(sym)
|
225
|
+
|
226
|
+
super
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Templates are rendered using this class. Templates are effectively treated
|
231
|
+
# as methods of this class when rendered. So any methods or attributes exposed
|
232
|
+
# in this class are available inside of templates.
|
233
|
+
class TemplateContext
|
234
|
+
attr_accessor :page
|
235
|
+
delegate :data, :sitemap, :config, to: :@config_obj
|
236
|
+
|
237
|
+
def initialize(target, mode, config)
|
238
|
+
# Target is development or production
|
239
|
+
@target = target.to_sym
|
240
|
+
# Mode is build or server
|
241
|
+
@mode = mode.to_sym
|
242
|
+
# Config is our Builder config object
|
243
|
+
@config_obj = config
|
244
|
+
# Page is a hash representing the page. Is the same as whats in sitemap
|
245
|
+
# for a given page path
|
246
|
+
@page = {}
|
247
|
+
# Locals is a hash thats used to resolve missing methods, making them
|
248
|
+
# seem like local variables
|
249
|
+
@locals = {}
|
250
|
+
end
|
251
|
+
|
252
|
+
# Render any given template and return as a string. Can be used to render
|
253
|
+
# partials.
|
254
|
+
def render(template_path, locals = {})
|
255
|
+
old_locals = @locals
|
256
|
+
@locals = locals.with_indifferent_access
|
257
|
+
erb = ERB.new(File.read(template_path), trim_mode: '-')
|
258
|
+
erb.filename = File.expand_path(template_path)
|
259
|
+
ret = erb.result(binding)
|
260
|
+
@locals = old_locals
|
261
|
+
ret
|
262
|
+
end
|
263
|
+
|
264
|
+
# Load the content of a pre-rendered file and return it
|
265
|
+
def include_file(filepath)
|
266
|
+
content = File.read(filepath)
|
267
|
+
mime = MimeMagic.by_path(filepath)
|
268
|
+
if mime.text?
|
269
|
+
return content
|
270
|
+
elsif mime.image?
|
271
|
+
return Base64.strict_encode64(content)
|
272
|
+
else
|
273
|
+
raise "File '${filepath}' of type '${mime}' can't be included as text"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# HELPERS
|
278
|
+
|
279
|
+
# Get the full URL to the root of this site
|
280
|
+
def http_prefix
|
281
|
+
return '/' if server? && development?
|
282
|
+
prefix = config[:http_prefix] || '/'
|
283
|
+
prefix += '/' unless prefix =~ %r{/$}
|
284
|
+
prefix
|
285
|
+
end
|
286
|
+
|
287
|
+
# Get the full URL to the root of where assets are stored
|
288
|
+
def asset_http_prefix
|
289
|
+
return '/' if server? && development?
|
290
|
+
prefix = config[:asset_http_prefix] || http_prefix
|
291
|
+
prefix += '/' unless prefix =~ %r{/$}
|
292
|
+
prefix
|
293
|
+
end
|
294
|
+
|
295
|
+
# Generate and return the URL to any asset
|
296
|
+
def asset_path(*args)
|
297
|
+
path = args.join('/')
|
298
|
+
page = sitemap[path]
|
299
|
+
if production? && page[:digest] == true
|
300
|
+
raise "Missing digest for #{path}" if page[:digest_path].blank?
|
301
|
+
path = page[:digest_path]
|
302
|
+
end
|
303
|
+
asset_http_prefix + path
|
304
|
+
end
|
305
|
+
|
306
|
+
# Generate and return the URL to any page
|
307
|
+
def canonical_url(*args)
|
308
|
+
http_prefix + args.join('/')
|
309
|
+
end
|
310
|
+
|
311
|
+
# Are we running as a server?
|
312
|
+
def server?
|
313
|
+
@mode == :server
|
314
|
+
end
|
315
|
+
|
316
|
+
# Are we building this app out?
|
317
|
+
def build?
|
318
|
+
@mode == :build
|
319
|
+
end
|
320
|
+
|
321
|
+
# Is this in production?
|
322
|
+
def production?
|
323
|
+
@target == :production
|
324
|
+
end
|
325
|
+
|
326
|
+
# Is this in development?
|
327
|
+
def development?
|
328
|
+
@target == :development
|
329
|
+
end
|
330
|
+
|
331
|
+
# Looks for an invoked method name in locals then in config, so locals
|
332
|
+
# and config vars can be used as if they're local vars in the template
|
333
|
+
def method_missing(sym)
|
334
|
+
return @locals[sym] if @locals.key?(sym)
|
335
|
+
return config[sym] if config.key?(sym)
|
336
|
+
super
|
337
|
+
end
|
338
|
+
|
339
|
+
def respond_to_missing?(sym, *)
|
340
|
+
@locals.key?(sym) || config.key?(sym) || super
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
private
|
345
|
+
|
346
|
+
# Generate one page from the sitemap and save to `build/`
|
347
|
+
def build_page(path, ctx, silent: false)
|
348
|
+
ctx.page = sitemap[path]
|
349
|
+
out_fname = File.join(BUILD_DIR, path)
|
350
|
+
|
351
|
+
# Check page data for info on how to build this path
|
352
|
+
if ctx.page['template'].present?
|
353
|
+
content = ctx.render(ctx.page['template'])
|
354
|
+
elsif ctx.page['json'].present?
|
355
|
+
content = ctx.page['json'].to_json
|
356
|
+
elsif ctx.page['yaml'].present?
|
357
|
+
content = ctx.page['yaml'].to_yaml
|
358
|
+
elsif ctx.page['file'].present?
|
359
|
+
content = File.read(ctx.page['file'])
|
360
|
+
else
|
361
|
+
raise(
|
362
|
+
ArgumentError,
|
363
|
+
"Page '#{path}' missing one of required attributes: 'template', 'json', 'yaml', 'file'."
|
364
|
+
)
|
365
|
+
end
|
366
|
+
|
367
|
+
# Check if we have a layout defined, use it
|
368
|
+
layout_fname = ctx.page['layout'] || config['layout']
|
369
|
+
content = ctx.render(layout_fname) { content } if layout_fname.present?
|
370
|
+
|
371
|
+
# If page data includes a digest flag, add sha1 digest to output filename
|
372
|
+
if ctx.page['digest'] == true
|
373
|
+
ext = Builder.fullextname(path)
|
374
|
+
fname = File.basename(path, ext)
|
375
|
+
dir = File.dirname(path)
|
376
|
+
digest = Digest::SHA1.hexdigest(content)
|
377
|
+
digest_fname = "#{fname}-#{digest}#{ext}"
|
378
|
+
ctx.page['digest_path'] = "#{dir}/#{digest_fname}"
|
379
|
+
out_fname = File.join(BUILD_DIR, dir, digest_fname)
|
380
|
+
end
|
381
|
+
|
382
|
+
puts "Writing #{out_fname}..." unless silent
|
383
|
+
FileUtils.mkdir_p(File.dirname(out_fname))
|
384
|
+
File.write(out_fname, content)
|
385
|
+
content
|
386
|
+
end
|
387
|
+
|
388
|
+
# Read json and yaml files from `data/` and load them into a Hash using the
|
389
|
+
# basename of the file names.
|
390
|
+
def load_data
|
391
|
+
data = {}.with_indifferent_access
|
392
|
+
|
393
|
+
Dir.glob("#{DATA_DIR}/*.json") do |fname|
|
394
|
+
key = File.basename(fname, '.json').to_sym
|
395
|
+
puts "Loading data[:#{key}] from #{fname}..."
|
396
|
+
data[key] = JSON.parse(File.read(fname))
|
397
|
+
end
|
398
|
+
|
399
|
+
Dir.glob("#{DATA_DIR}/*.yaml") do |fname|
|
400
|
+
key = File.basename(fname, '.yaml').to_sym
|
401
|
+
puts "Loading data[:#{key}] from #{fname}..."
|
402
|
+
data[key] = Psych.parse(fname)
|
403
|
+
end
|
404
|
+
|
405
|
+
data
|
406
|
+
end
|
407
|
+
|
408
|
+
# Execute all blocks in config registered with the given hook name.
|
409
|
+
def run_hook!(name)
|
410
|
+
return unless hooks[name.to_sym]
|
411
|
+
hooks[name.to_sym].each { |blk| config.instance_exec(&blk) }
|
412
|
+
end
|
413
|
+
|
414
|
+
# Find prebuilt assets and add them to the sitemap
|
415
|
+
def index_prebuilt!
|
416
|
+
Dir.glob("#{PREBUILT_DIR}/**/[^_]*.*") do |filename|
|
417
|
+
sitemap[filename.sub("#{PREBUILT_DIR}/", '')] = { file: filename, digest: true }
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
data/vizbuilder.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = 'vizbuilder'
|
6
|
+
spec.version = File.read('VERSION').strip
|
7
|
+
spec.authors = ['Ryan Mark']
|
8
|
+
spec.email = ['ryan@mrk.cc']
|
9
|
+
|
10
|
+
spec.summary = 'Simple and fast static site generator'
|
11
|
+
spec.homepage = 'https://github.com/ryanmark/vizbuilder-ruby'
|
12
|
+
|
13
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
14
|
+
f.match(%r{^(test|spec|features)/})
|
15
|
+
end
|
16
|
+
spec.require_paths = ['lib']
|
17
|
+
|
18
|
+
spec.add_dependency 'activesupport', '~> 5.2'
|
19
|
+
spec.add_dependency 'mimemagic', '~> 0.3'
|
20
|
+
spec.add_dependency 'rack', '~> 2.0'
|
21
|
+
spec.add_dependency 'ruby_dig', '~> 0.0.2'
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler'
|
24
|
+
spec.add_development_dependency 'minitest', '~> 5.11'
|
25
|
+
spec.add_development_dependency 'rake', '~> 12.3'
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vizbuilder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ryan Mark
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-04-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mimemagic
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: ruby_dig
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.0.2
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.0.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '5.11'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '5.11'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '12.3'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '12.3'
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- ryan@mrk.cc
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rubocop.yml"
|
120
|
+
- CHANGELOG.md
|
121
|
+
- Gemfile
|
122
|
+
- LICENSE
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- VERSION
|
126
|
+
- do_release.sh
|
127
|
+
- lib/viz-builder.rb
|
128
|
+
- lib/viz_builder.rb
|
129
|
+
- lib/vizbuilder.rb
|
130
|
+
- vizbuilder.gemspec
|
131
|
+
homepage: https://github.com/ryanmark/vizbuilder-ruby
|
132
|
+
licenses: []
|
133
|
+
metadata: {}
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubygems_version: 3.0.1
|
150
|
+
signing_key:
|
151
|
+
specification_version: 4
|
152
|
+
summary: Simple and fast static site generator
|
153
|
+
test_files: []
|