mascot 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 19f9e7b959947630495440f3a2c7c728dcb02af7
4
+ data.tar.gz: 88f1c9212234555876648f7f77952d6cd3b47fbc
5
+ SHA512:
6
+ metadata.gz: 04c0b817aad5d980a610c88f3e772486d195c4a75ad8b4c16c2326d4159c32f3fe37584f5cb9b990dfefa944cdb54abf84269608985ccf1f9014d8d6518c433a
7
+ data.tar.gz: 3bb29074fdd9c5df6a7bed480b9c748bcce654a5ff8d4a115b6cbe21ef9889ac0ab9f692da952ffa9a8ca63f4074362798f1be8aca2c06c8b5bd6f2b62e9cd95
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.11.2
5
+ script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mascot.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,70 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ # Note: The cmd option is now required due to the increasing number of ways
19
+ # rspec may be run, below are examples of the most common uses.
20
+ # * bundler: 'bundle exec rspec'
21
+ # * bundler binstubs: 'bin/rspec'
22
+ # * spring: 'bin/rspec' (This will use spring if running and you have
23
+ # installed the spring binstubs per the docs)
24
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
25
+ # * 'just' rspec: 'rspec'
26
+
27
+ guard :rspec, cmd: "bundle exec rspec" do
28
+ require "guard/rspec/dsl"
29
+ dsl = Guard::RSpec::Dsl.new(self)
30
+
31
+ # Feel free to open issues for suggestions and improvements
32
+
33
+ # RSpec files
34
+ rspec = dsl.rspec
35
+ watch(rspec.spec_helper) { rspec.spec_dir }
36
+ watch(rspec.spec_support) { rspec.spec_dir }
37
+ watch(rspec.spec_files)
38
+
39
+ # Ruby files
40
+ ruby = dsl.ruby
41
+ dsl.watch_spec_files_for(ruby.lib_files)
42
+
43
+ # Rails files
44
+ rails = dsl.rails(view_extensions: %w(erb haml slim))
45
+ dsl.watch_spec_files_for(rails.app_files)
46
+ dsl.watch_spec_files_for(rails.views)
47
+
48
+ watch(rails.controllers) do |m|
49
+ [
50
+ rspec.spec.("routing/#{m[1]}_routing"),
51
+ rspec.spec.("controllers/#{m[1]}_controller"),
52
+ rspec.spec.("acceptance/#{m[1]}")
53
+ ]
54
+ end
55
+
56
+ # Rails config changes
57
+ watch(rails.spec_helper) { rspec.spec_dir }
58
+ watch(rails.routes) { "#{rspec.spec_dir}/routing" }
59
+ watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
60
+
61
+ # Capybara features specs
62
+ watch(rails.view_dirs) { |m| rspec.spec.("features/#{m[1]}") }
63
+ watch(rails.layouts) { |m| rspec.spec.("features/#{m[1]}") }
64
+
65
+ # Turnip features and steps
66
+ watch(%r{^spec/acceptance/(.+)\.feature$})
67
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
68
+ Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
69
+ end
70
+ end
data/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # Mascot
2
+
3
+ Mascot is a file-backed website content manager that can be embedded in popular web frameworks like Rails, run stand-alone, or be compiled into static sites.
4
+
5
+ [![Build Status](https://travis-ci.org/bradgessler/mascot.svg?branch=master)](https://travis-ci.org/bradgessler/mascot)
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'mascot'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install mascot
22
+
23
+ ## Usage
24
+
25
+ Given a haml file like:
26
+
27
+ ```haml
28
+ ---
29
+ title: Name
30
+ meta:
31
+ keywords: One
32
+ ---
33
+
34
+ !!!
35
+ %html
36
+ %head
37
+ %title=current_page.data["title"]
38
+ %body
39
+ %h1 Hi
40
+ %p This is just some content
41
+ %h2 There
42
+ ```
43
+
44
+ Mascot can parse out the frontmatter and body to render inside your framework of choice, like Rails:
45
+
46
+ ```ruby
47
+ class MascotController < ApplicationController
48
+ # TODO: Isolate this integration into a rails engine. Copy
49
+ # the way HighVoltage provides routes and scopes everything.
50
+ def show
51
+ sitemap = Rails.application.config.sitemap
52
+
53
+ resource = sitemap.find_by_request_path(request.path)
54
+ if resource
55
+ # TODO: Implement a whitelisted, chained handler for
56
+ # the template type with ActionView::Template::Handlers.extensions
57
+ template_type = resource.file_path.extname.delete(".")
58
+ @_locals = resource.locals.merge(sitemap: sitemap)
59
+ layout = resource.data.fetch("layout", "high_voltage")
60
+ render inline: resource.body, type: template_type, layout: layout, locals: @_locals
61
+ else
62
+ render status: :not_found, text: "#{request.path} Not Found"
63
+ end
64
+ end
65
+ end
66
+ ```
67
+
68
+ so when you call `current_page.data` from your templates, you get something like this:
69
+
70
+ ```irb
71
+ > current_page.data
72
+ => {"title"=>"Name", "meta"=>{"keywords"=>"One"}, "toc"=>["Hi", "There"]}
73
+ > current_page.data.dig("meta", "keywords")
74
+ => "One"
75
+ ```
76
+
77
+ Mascot is designed to be embedded in rails and other Ruby web frameworks.
78
+
79
+ # Features
80
+
81
+ Mascot implements a subset of the best features from the [Middleman](http://www.middlemanapp.com/) static site generator including the Sitemap and Frontmatter.
82
+
83
+ ## Frontmatter
84
+
85
+ Frontmatter is a way to attach metadata to content pages. Its a powerful way to enable a team of writers and engineers work together on content. The engineers focus on reading values from frontmatter while the writers can change values.
86
+
87
+ ```haml
88
+ ---
89
+ title: This is a swell doc
90
+ meta:
91
+ keywords: this, is, a, test
92
+ background_color: #0f0
93
+ ---
94
+
95
+ %html
96
+ %head
97
+ %meta(name="keywords" value="#{current_page.data.dig("meta", "keywords")}")
98
+ %body(style="background: #{current_page.data["background_color"]};")
99
+ %h1=current_page.data["title"]
100
+ %p And here's the rest of the content!
101
+ ```
102
+
103
+ ## Sitemap
104
+
105
+ The Sitemap accepts a directory path
106
+
107
+ ```irb
108
+ > sitemap = Mascot::Sitemap.new(file_path: "spec/pages")
109
+ => #<Mascot::Sitemap:0x007fcd24103710 @file_path=#<Pathname:spec/pages>, @request_path=#<Pathname:/>>
110
+ ```
111
+
112
+ Then you can request a resource by request path:
113
+
114
+ ```irb
115
+ > resource = sitemap.find_by_request_path("/test")
116
+ => #<Mascot::Resource:0x007fcd2488a128 @request_path="/test", @content_type="text/html", @file_path=#<Pathname:spec/pages/test.html.haml>, @frontmatter=#<Mascot::Frontmatter:0x007fcd24889e80 @data="title: Name\nmeta:\n keywords: One", @body="\n!!!\n%html\n %head\n %title=current_page.data[\"title\"]\n %body\n %h1 Hi\n %p This is just some content\n %h2 There\n">>
117
+ ```
118
+
119
+ And access the frontmatter data (if available) and body of the template.
120
+
121
+ ```irb
122
+ > resource.data
123
+ => {"title"=>"Name", "meta"=>{"keywords"=>"One"}}
124
+ > resource.body
125
+ => "\n!!!\n%html\n %head\n %title=current_page.data[\"title\"]\n %body\n %h1 Hi\n %p This is just some content\n %h2 There\n"
126
+ ```
127
+
128
+ ### Resource globbing
129
+
130
+ The Sitemap API is a powerful way to query content via resource globbing. For example, if you have a folder full of files but you only want all `.html` files within the `docs` directory, you'd do something like:
131
+
132
+ ```haml
133
+ %ol
134
+ -sitemap.resources("docs/*.html*").each do |page|
135
+ %li=link_to page.data["title"], page.request_path
136
+ ```
137
+
138
+ ## Development
139
+
140
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
141
+
142
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
143
+
144
+ ## Contributing
145
+
146
+ Bug reports and pull requests are welcome on GitHub at https://github.com/bradgessler/mascot.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "mascot"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/config.ru ADDED
@@ -0,0 +1,3 @@
1
+ require "mascot"
2
+
3
+ run Mascot::Server.glob("spec/pages/*")
@@ -0,0 +1,3 @@
1
+ module Mascot
2
+ VERSION = "0.1.0"
3
+ end
data/lib/mascot.rb ADDED
@@ -0,0 +1,136 @@
1
+ require "mascot/version"
2
+
3
+ module Mascot
4
+ require "yaml"
5
+
6
+ # Parses metadata from the header of the page.
7
+ class Frontmatter
8
+ DELIMITER = "---".freeze
9
+ PATTERN = /\A(#{DELIMITER}\n(.+)\n#{DELIMITER}\n)?(.+)\Z/m
10
+
11
+ attr_reader :body
12
+
13
+ def initialize(content)
14
+ _, @data, @body = content.match(PATTERN).captures
15
+ end
16
+
17
+ def data
18
+ @data ? YAML.load(@data) : {}
19
+ end
20
+
21
+ private
22
+ def parse
23
+ @content
24
+ end
25
+ end
26
+
27
+ # Represents a page in a web server context.
28
+ class Resource
29
+ # TODO: I don't like the Binding, locals, and frontmatter
30
+ # being in the Resource. That should be moved to a page
31
+ # object and be delegated to that. Or perhaps the page body?
32
+ # I'm moving forward with this now though to keep the objects simpler.
33
+ # We'll see how it evolves.
34
+ Binding = Struct.new(:data)
35
+
36
+ CONTENT_TYPE = "text/html".freeze
37
+
38
+ attr_reader :request_path, :file_path, :content_type
39
+
40
+ extend Forwardable
41
+ def_delegators :@frontmatter, :data, :body
42
+
43
+ def initialize(request_path: , file_path: , content_type: CONTENT_TYPE)
44
+ @request_path = request_path
45
+ @content_type = content_type
46
+ @file_path = Pathname.new file_path
47
+ @frontmatter = Frontmatter.new File.read @file_path
48
+ end
49
+
50
+ # Locals that should be merged into or given to the rendering context.
51
+ def locals
52
+ { current_page: Binding.new(data) }
53
+ end
54
+ end
55
+
56
+ # A collection of pages from a directory.
57
+ class Sitemap
58
+ # Default file pattern to pick up in sitemap
59
+ DEFAULT_GLOB = "**/*.*".freeze
60
+ # Default root path for sitemap.
61
+ DEFAULT_ROOT_DIR = Pathname.new(".").freeze
62
+ # Default root request path
63
+ DEFAULT_ROOT_REQUEST_PATH = Pathname.new("/").freeze
64
+
65
+ def initialize(file_path: DEFAULT_ROOT_DIR, request_path: DEFAULT_ROOT_REQUEST_PATH)
66
+ @file_path = Pathname.new(file_path)
67
+ @request_path = Pathname.new(request_path)
68
+ end
69
+
70
+ # Lazy stream of resources.
71
+ def resources(glob = DEFAULT_GLOB)
72
+ Dir[@file_path.join(glob)].lazy.map do |path|
73
+ Resource.new request_path: request_path(path), file_path: path
74
+ end
75
+ end
76
+
77
+ # Find the page with a path.
78
+ def find_by_request_path(request_path)
79
+ resources.find { |r| r.request_path == request_path }
80
+ end
81
+
82
+ private
83
+ # Given a @file_path of `/hi`, this method changes `/hi/there/friend.html.erb`
84
+ # to an absolute `/there/friend` format by removing the file extensions
85
+ def request_path(path)
86
+ # Relative path of resource to the file_path of this project.
87
+ relative_path = Pathname.new(path).relative_path_from(@file_path)
88
+ # Removes the .fooz.baz
89
+ @request_path.join(relative_path).to_s.sub(/\..*/, '')
90
+ end
91
+ end
92
+
93
+ require "tilt"
94
+
95
+ class TiltRenderer
96
+ def initialize(resource)
97
+ @resource = resource
98
+ end
99
+
100
+ def render
101
+ template = engine.new { |t| @resource.body }
102
+ template.render(Object.new, @resource.locals)
103
+ end
104
+
105
+ private
106
+ def engine
107
+ Tilt[@resource.file_path]
108
+ end
109
+ end
110
+
111
+ # Mount inside of a config.ru file to run this as a server.
112
+ class Server
113
+ ROOT_PATH = Pathname.new("/")
114
+
115
+ def initialize(sitemap, relative_to: "/")
116
+ @relative_to = Pathname.new(relative_to)
117
+ @sitemap = sitemap
118
+ end
119
+
120
+ def call(env)
121
+ req = Rack::Request.new(env)
122
+ if resource = @sitemap.find_by_request_path(normalize_path(req.path))
123
+ [ 200, {"Content-Type" => resource.content_type}, [TiltRenderer.new(resource).render] ]
124
+ else
125
+ [ 404, {"Content-Type" => "text/plain"}, ["Not Found"]]
126
+ end
127
+ end
128
+
129
+ private
130
+ # If we mount this middleware in a path other than root, we need to configure it
131
+ # so that it correctly maps the request path to the content path.
132
+ def normalize_path(request_path)
133
+ ROOT_PATH.join(Pathname.new(request_path).relative_path_from(@relative_to)).to_s
134
+ end
135
+ end
136
+ end
data/mascot.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mascot/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mascot"
8
+ spec.version = Mascot::VERSION
9
+ spec.authors = ["Brad Gessler"]
10
+ spec.email = ["bradgessler@gmail.com"]
11
+
12
+ spec.summary = %q{An experiment in data driven static website generation.}
13
+ spec.homepage = "https://github.com/bradgessler/mascot"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.bindir = "exe"
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.11"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "rspec", "~> 3.0"
23
+ spec.add_development_dependency "haml", "~> 4.0"
24
+ spec.add_development_dependency "guard-rspec"
25
+ spec.add_development_dependency "rack-test"
26
+ spec.add_development_dependency "rack"
27
+ spec.add_development_dependency "actionpack", "~> 4.2"
28
+
29
+ spec.add_dependency "tilt"
30
+ end
metadata ADDED
@@ -0,0 +1,183 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mascot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Brad Gessler
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-07-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: haml
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
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: rack-test
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: actionpack
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '4.2'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '4.2'
125
+ - !ruby/object:Gem::Dependency
126
+ name: tilt
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description:
140
+ email:
141
+ - bradgessler@gmail.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".rspec"
148
+ - ".travis.yml"
149
+ - Gemfile
150
+ - Guardfile
151
+ - README.md
152
+ - Rakefile
153
+ - bin/console
154
+ - bin/setup
155
+ - config.ru
156
+ - lib/mascot.rb
157
+ - lib/mascot/version.rb
158
+ - mascot.gemspec
159
+ homepage: https://github.com/bradgessler/mascot
160
+ licenses: []
161
+ metadata: {}
162
+ post_install_message:
163
+ rdoc_options: []
164
+ require_paths:
165
+ - lib
166
+ required_ruby_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ required_rubygems_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ requirements: []
177
+ rubyforge_project:
178
+ rubygems_version: 2.5.1
179
+ signing_key:
180
+ specification_version: 4
181
+ summary: An experiment in data driven static website generation.
182
+ test_files: []
183
+ has_rdoc: