middleman-webcomic 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/.gitignore +5 -0
  2. data/ChangeLog.md +11 -0
  3. data/Gemfile +3 -0
  4. data/License +18 -0
  5. data/Rakefile +22 -0
  6. data/Readme.md +22 -0
  7. data/lib/middleman-webcomic/cli.rb +5 -0
  8. data/lib/middleman-webcomic/data.rb +213 -0
  9. data/lib/middleman-webcomic/feature.rb +172 -0
  10. data/lib/middleman-webcomic/mock_app.rb +31 -0
  11. data/lib/middleman-webcomic/pager.rb +51 -0
  12. data/lib/middleman-webcomic/tasks.rb +53 -0
  13. data/lib/middleman-webcomic/template/Gemfile.tt +7 -0
  14. data/lib/middleman-webcomic/template/Rakefile.tt +28 -0
  15. data/lib/middleman-webcomic/template/config.ru +16 -0
  16. data/lib/middleman-webcomic/template/config.tt +71 -0
  17. data/lib/middleman-webcomic/template/source/about.html.slim +11 -0
  18. data/lib/middleman-webcomic/template/source/comics/index.html.slim +14 -0
  19. data/lib/middleman-webcomic/template/source/comics/template.html.slim +1 -0
  20. data/lib/middleman-webcomic/template/source/feed.xml.builder +30 -0
  21. data/lib/middleman-webcomic/template/source/index.html.slim +1 -0
  22. data/lib/middleman-webcomic/template/source/layout.slim +63 -0
  23. data/lib/middleman-webcomic/template/source/robots.txt +3 -0
  24. data/lib/middleman-webcomic/template/source/theme/_comic.slim +4 -0
  25. data/lib/middleman-webcomic/template/source/theme/_layout_tall.slim +17 -0
  26. data/lib/middleman-webcomic/template/source/theme/_layout_wide.slim +16 -0
  27. data/lib/middleman-webcomic/template/source/theme/_sidebar.slim +13 -0
  28. data/lib/middleman-webcomic/template/source/theme/images/favicon.png +0 -0
  29. data/lib/middleman-webcomic/template/source/theme/scripts/app.js.coffee +4 -0
  30. data/lib/middleman-webcomic/template/source/theme/styles/_utils.scss +399 -0
  31. data/lib/middleman-webcomic/template/source/theme/styles/screen.css.sass +89 -0
  32. data/lib/middleman-webcomic/template.rb +30 -0
  33. data/lib/middleman-webcomic/version.rb +7 -0
  34. data/lib/middleman-webcomic.rb +11 -0
  35. data/lib/middleman_init.rb +1 -0
  36. data/middleman-webcomic.gemspec +29 -0
  37. metadata +113 -0
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ *.gem
3
+ *.lock
4
+ examples/**/output
5
+ .sass-cache
data/ChangeLog.md ADDED
@@ -0,0 +1,11 @@
1
+ # v0.5.0
2
+ - Updated to support Middleman's new auto-discovery.
3
+ - Includes boilerplate site.
4
+
5
+ # v0.4.0
6
+ - Added storyline support and helpers.
7
+
8
+ This introduces a breaking change to how the data is added to middle data_content, fortunately I'm the only one using it right now. :)
9
+
10
+ # v0.2.1
11
+ - Initial release. Yay!
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/License ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (C) 2011 by Matt McCray
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
2
+ require "middleman-webcomic/version"
3
+
4
+ desc "builds gem"
5
+ task :build do
6
+ system "gem build middleman-webcomic.gemspec"
7
+ end
8
+
9
+ desc "releases gem"
10
+ task :release => :build do
11
+ system "gem push middleman-webcomic-#{Middleman::Webcomic::VERSION}.gem"
12
+ end
13
+
14
+ desc "installs gem"
15
+ task :install => :build do
16
+ system "gem install middleman-webcomic-#{Middleman::Webcomic::VERSION}"
17
+ end
18
+
19
+ desc "uninstalls gem"
20
+ task :uninstall do
21
+ system "gem uninstall middleman-webcomic"
22
+ end
data/Readme.md ADDED
@@ -0,0 +1,22 @@
1
+ # Webcomic for MiddleMan
2
+
3
+ *Updated for Middleman auto-discovery.*
4
+
5
+ After you install the gem:
6
+
7
+ gem install middleman-webcomic
8
+
9
+ Create a new webcomic site like this:
10
+
11
+ middleman init MY_SITE --template=webcomic
12
+
13
+ Then customize to your satisfaction.
14
+
15
+ # Live Sites
16
+
17
+ This extension is used in conjunction with MiddleMan on the following sites:
18
+
19
+ * [ZooDotCom](http://www.zoodotcom.com)
20
+ * [Tactless Comics](http://tactless.inkwellian.com)
21
+ * [Darkshore Detectives](http://darkshore.inkwellian.com)
22
+
@@ -0,0 +1,5 @@
1
+
2
+ # To use: Thor
3
+ #
4
+ # mm-webcomic init|new|check
5
+ #
@@ -0,0 +1,213 @@
1
+ require 'thor'
2
+ require 'date'
3
+
4
+ class String
5
+ def create_slug
6
+ s= self.downcase.gsub(/'/, '').gsub(/[^a-z0-9]+/, '-')
7
+ s.chop! if s[-1] == '-'
8
+ s
9
+ end
10
+ end
11
+
12
+ module Middleman
13
+
14
+ module Webcomic
15
+
16
+ def self.load_from(path, app)
17
+ all= []
18
+ Dir["#{path}/*.comic*"].each do |filename| # Hardcoded for markdown... For now
19
+ comic = Comic.new(filename, app)
20
+ #puts comic.pub_date.inspect
21
+ all << comic if comic.publishable?
22
+ end
23
+
24
+ if app.settings.webcomic_sort_by == :publish_date
25
+ all.sort! {|x,y| y.pub_date <=> x.pub_date }
26
+ else
27
+ fld= app.settings.webcomic_sort_by
28
+ all.sort! {|x,y| y[fld] <=> x[fld] }
29
+ end
30
+
31
+ # Update all the position info...
32
+ cl= all.length
33
+ all.each_with_index do |comic, i|
34
+ comic[:position]= (cl - i)
35
+ comic[:index]= i
36
+ comic.first= all[-1]
37
+ comic.last= all[0]
38
+ comic.next= i > 0 ? all[i - 1] : nil
39
+ comic.prev= i < (cl - 1) ? all[i + 1] : nil
40
+ if app.settings.webcomic_enable_stories and comic.story
41
+ Story.find_or_create_for comic
42
+ end
43
+ end
44
+
45
+ stories= Story.all
46
+ stories.sort! {|x,y| y.pub_date <=> x.pub_date }
47
+ sl= stories.length
48
+ stories.each_with_index do |story, i|
49
+ story.position= (sl - i)
50
+ # puts "##{story.position} - #{story.title}(#{story.slug}): #{story.comics.length}"
51
+ end
52
+
53
+ [all, stories]
54
+ end
55
+
56
+ class Story
57
+ attr_accessor :title, :slug, :comics, :position
58
+
59
+ def initialize(title)
60
+ @title= title
61
+ @slug= @title.create_slug
62
+ @comics= []
63
+ @position= nil
64
+ end
65
+
66
+ def pub_date
67
+ @comics.last.pub_date
68
+ end
69
+
70
+ def add_comic(comic)
71
+ @comics << comic
72
+ end
73
+
74
+ class << self
75
+
76
+ def all
77
+ @stories ||= {}
78
+ @stories.values
79
+ end
80
+
81
+ def find_or_create_for(comic)
82
+ title= comic.story
83
+ @stories ||= {}
84
+ unless @stories.has_key? title
85
+ story= @stories[title]= new(title)
86
+ end
87
+ @stories[title].add_comic comic
88
+ @stories[title]
89
+ end
90
+ end
91
+ end
92
+
93
+ class Comic
94
+
95
+ attr_accessor :content, :source, :metadata, :path, :next, :prev, :first, :last
96
+
97
+ def initialize(path, app)
98
+ @app= app
99
+ settings= app.settings
100
+ @path= path
101
+ @source= ""
102
+ @content= ""
103
+ @next= nil
104
+ @prev= nil
105
+ @first= nil
106
+ @last= nil
107
+ @metadata= ::Thor::CoreExt::HashWithIndifferentAccess.new({})
108
+ @metadata[:ext]= File.extname(path)
109
+ @metadata[:publish_date]= Date.today #Time.now.strftime "%Y-%m-%d"
110
+ @metadata[:slug]= File.basename(path).gsub( @metadata[:ext], '').gsub('.comic','')
111
+
112
+ if /^([\d]{2})([\d]{2})([\d]{2})\-(.*)(\.comic#{@metadata[:ext]})$/ =~ File.basename(path)
113
+ year= "20#{ $1 }"
114
+ month= $2
115
+ day= $3
116
+ @metadata[:publish_date]= Date.parse "#{year}-#{month}-#{day}"
117
+ @metadata[:slug]= $4
118
+ end
119
+
120
+ @metadata[:title]= @metadata[:slug].titleize rescue @metadata[:slug]
121
+ @metadata[:slug]= @metadata[:slug].downcase
122
+
123
+ # Parse yaml header...
124
+ begin
125
+ read_yaml()
126
+ rescue
127
+ puts "Error reading YAML! #{self.slug}"
128
+ end
129
+
130
+ if self.filename.nil?
131
+ throw "Filename Missing: Comics must define a filename! (#{self.slug})"
132
+ end
133
+
134
+ if publishable?
135
+ # Dont' waste time if it's not a publishable comic
136
+ engine= Tilt[path].new { @source }
137
+ @content= engine.render
138
+ end
139
+ end
140
+
141
+ def [](key)
142
+ @metadata[key]
143
+ end
144
+ def []=(key, value)
145
+ #puts "Setting #{key} to #{value}"
146
+ @metadata[key]= value
147
+ end
148
+
149
+ def method_missing(key, value=nil)
150
+ if @metadata.has_key? key
151
+ @metadata[key]
152
+ elsif @metadata.has_key? key.to_s
153
+ @metadata[key.to_s]
154
+ else
155
+ #super Should it throw an error?
156
+ nil
157
+ end
158
+ end
159
+
160
+ def to_json *a
161
+ data= @metadata.reject {|k,v| k == :ext or k == "ext" }
162
+ data.to_json *a
163
+ end
164
+
165
+ def publishable?
166
+ @is_publishable ||= begin
167
+ now= Time.now
168
+ now_string = now.strftime "%Y-%m-%d"
169
+ today= Date.parse(now_string)
170
+ pubdate= self.pub_date
171
+ case pubdate
172
+ when String
173
+ self['publish_date']= Date.parse(pubdate)
174
+ pubdate <= now_string
175
+ when Time
176
+ self['publish_date']= Date.parse(pubdate.strftime('%Y-%m-%d'))
177
+ pubdate <= now
178
+ when Date
179
+ pubdate <= today
180
+ else
181
+ true
182
+ end
183
+ end
184
+ end
185
+
186
+ def pub_date
187
+ date= self.publish_on || self.publish_date || self.date
188
+ case date
189
+ when String
190
+ Date.parse(date)
191
+ when Time
192
+ Date.parse(date.strftime('%Y-%m-%d'))
193
+ when Date
194
+ date
195
+ else
196
+ Date.today
197
+ end
198
+ end
199
+
200
+ private
201
+
202
+ def read_yaml()
203
+ @raw = File.read(@path)
204
+ data, source= @app.parse_front_matter @raw
205
+ data.each_pair do |k,v|
206
+ @metadata[k]= v
207
+ end
208
+ @source= source
209
+ end
210
+
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,172 @@
1
+ require 'fileutils'
2
+
3
+ module Middleman
4
+ module Features
5
+ module Webcomic
6
+ class << self
7
+
8
+ def registered(app)
9
+ puts "Webcomic v#{::Middleman::Webcomic::VERSION}"
10
+
11
+ # Default settings:
12
+ app.set :webcomic_images, "images/comics"
13
+ app.set :webcomic_source, "comics"
14
+ app.set :webcomic_source_images, "comics/images"
15
+ app.set :webcomic_source_type, :markdown
16
+ app.set :webcomic_uri, "comics"
17
+ app.set :webcomic_template, "/comics/template.html"
18
+ app.set :webcomic_domain, "http://MYSITE.dev" # ?
19
+
20
+ # ???
21
+ app.set :webcomic_enable_stories, false
22
+ app.set :webcomic_story_uri, "story"
23
+ app.set :webcomic_story_template, "/comics/story_template.html"
24
+
25
+ app.set :webcomic_enable_tags, false
26
+ app.set :webcomic_sort_by, :publish_date # or slug? and metadata
27
+ app.set :webcomic_slug_field, :slug
28
+
29
+ app.set :webcomic_include_comic_in_feed, true
30
+
31
+ app.helpers ::Middleman::Features::Webcomic::Helpers
32
+
33
+ app.after_configuration do
34
+ comics, stories= ::Middleman::Webcomic.load_from( File.join(app.root, app.settings.webcomic_source), app )
35
+
36
+ # puts comics.inspect
37
+
38
+ comics.each do |comic|
39
+ #puts "-> #{comic.filename} (#{comic.metadata.inspect})"
40
+ src_path= File.join(app.root, app.settings.webcomic_source_images, comic.filename)
41
+ tgt_path= File.join(app.views, app.settings.webcomic_images, comic.filename)
42
+
43
+ unless File.exists? tgt_path
44
+ if File.exists? src_path
45
+ puts "Moving into place: #{comic.filename}"
46
+ FileUtils.mkdir_p File.dirname(tgt_path), :verbose=>false
47
+ FileUtils.cp src_path, tgt_path, :verbose=>false
48
+ else
49
+ puts "MISSING COMIC: #{comic.filename}"
50
+ end
51
+ end
52
+
53
+ slug_field= app.settings.webcomic_slug_field
54
+ if File.exists? tgt_path
55
+ #puts "Building: /#{app.settings.webcomic_uri}/#{comic[slug_field]}.html"
56
+ app.page "/#{app.settings.webcomic_uri}/#{comic[slug_field]}.html", :proxy=>app.settings.webcomic_template, :ignore=>true do
57
+ @comic= comic
58
+ end
59
+ else
60
+ puts "Skipping #{comic.filename} (#{comic[slug_field]})"
61
+ end
62
+ end
63
+
64
+ if app.settings.webcomic_enable_stories
65
+ stories.each do |story|
66
+ app.page "/#{app.settings.webcomic_story_uri}/#{story.slug}.html", :proxy=>app.settings.webcomic_story_template, :ignore=>true do
67
+ @story= story
68
+ end
69
+ end
70
+ end
71
+
72
+ app.data_content 'webcomic', {
73
+ :strips=>comics,
74
+ :stories=>stories
75
+ }
76
+ end
77
+ end
78
+ alias :included :registered
79
+
80
+ end
81
+
82
+ module Helpers
83
+ def current_comic
84
+ (@comic || last_comic)
85
+ end
86
+
87
+ def first_comic
88
+ data.webcomic.strips.last
89
+ end
90
+
91
+ def last_comic
92
+ data.webcomic.strips.first
93
+ end
94
+
95
+ def next_comic
96
+ current_comic.next
97
+ end
98
+
99
+ def prev_comic
100
+ current_comic.prev
101
+ end
102
+
103
+ def comic_image_url(comic)
104
+ "/#{settings.webcomic_images}/#{comic.filename}"
105
+ end
106
+ alias :comic_image_for :comic_image_url
107
+
108
+ def current_comic_image
109
+ comic_image_for current_comic
110
+ end
111
+ alias :current_comic_image_url :current_comic_image
112
+
113
+ def comic_path_url(comic)
114
+ "/#{settings.webcomic_uri}/#{comic[settings.webcomic_slug_field]}"
115
+ end
116
+ alias :comic_path_for :comic_path_url
117
+
118
+ def current_comic_path
119
+ comic_path_for current_comic
120
+ end
121
+
122
+ def first_comic_path
123
+ comic_path_for first_comic
124
+ end
125
+
126
+ def last_comic_path
127
+ comic_path_for last_comic
128
+ end
129
+
130
+ def next_comic_path
131
+ comic_path_for next_comic
132
+ end
133
+
134
+ def prev_comic_path
135
+ comic_path_for prev_comic
136
+ end
137
+
138
+
139
+ # Story Helpers
140
+ def start_story_path_for(object)
141
+ story = if object.is_a? Story
142
+ object
143
+ else # it's a comic... right?
144
+ data.webcomic.stories.detect {|s| s.title == object.story }
145
+ end
146
+ comic_path_for story.comics.last
147
+ end
148
+
149
+ def story_path_for(object)
150
+ story = if object.is_a? Story
151
+ object
152
+ else # it's a comic... right?
153
+ data.webcomic.stories.detect {|s| s.title == object.story }
154
+ end
155
+ "/#{settings.webcomic_story_uri}/#{story.slug}"
156
+ end
157
+
158
+ # Feed Helpers
159
+
160
+ def webcomic_feed_data
161
+ data.webcomic.strips[0...10]
162
+ end
163
+
164
+ def feed_absolute_paths(source, path=settings.webcomic_domain)
165
+ source.gsub( /src=(["']*)\//) do |match|
166
+ "#{match[0...-1]}#{path}/"
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,31 @@
1
+ require 'middleman'
2
+ require 'slim'
3
+ require 'tilt'
4
+
5
+ module Middleman
6
+ module Webcomic
7
+ class MockApp
8
+
9
+ attr_reader :settings
10
+
11
+ def initialize(settings={})
12
+ @settings= ::Thor::CoreExt::HashWithIndifferentAccess.new(settings)
13
+ end
14
+
15
+ def method_missing(key, value=nil)
16
+ if @settings.has_key? key
17
+ @settings[key]
18
+ elsif @settings.has_key? key.to_s
19
+ @settings[key.to_s]
20
+ else
21
+ #super Should it throw an error?
22
+ nil
23
+ end
24
+ end
25
+
26
+ def parse_front_matter(content)
27
+ Middleman::CoreExtensions::FrontMatter.parse_front_matter(content)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,51 @@
1
+ module Middleman
2
+ module Webcomic
3
+ class Pager
4
+ attr_reader :all, :pages, :base_url, :current_page, :page_sets
5
+
6
+ def initialize(articles, base_path="/page", page_size=5)
7
+ @all= articles
8
+ @page_size= page_size
9
+ @base_path= base_path
10
+ @page_sets= @all.in_groups_of(page_size, false)
11
+ @pages= []
12
+ @current_page=1
13
+ @page_sets.each do |art_ary|
14
+ @pages << Thor::CoreExt::HashWithIndifferentAccess.new({
15
+ 'articles' => art_ary,
16
+ 'page' => @current_page,
17
+ 'uri' => "#{base_path}/#{current_page}",
18
+ 'pager' => self
19
+ })
20
+ @current_page += 1
21
+ end
22
+ @current_page= nil
23
+ end
24
+
25
+ def length
26
+ @pages.length
27
+ end
28
+
29
+ def first
30
+ @pages.first
31
+ end
32
+
33
+ def last
34
+ @pages.last
35
+ end
36
+
37
+ def each
38
+ @current_page=1
39
+ @pages.each do |page_set|
40
+ yield page_set
41
+ @current_page += 1
42
+ end
43
+ @current_page= nil
44
+ end
45
+
46
+ def [](key)
47
+ @pages[key]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,53 @@
1
+ # Rake tasks
2
+ require 'rake'
3
+
4
+ def ask message
5
+ print message
6
+ STDIN.gets.chomp
7
+ end
8
+
9
+ NOTES_PATH= File.expand_path "./comics/notes" unless defined?(NOTES_PATH)
10
+ IMAGE_PATH= File.expand_path "./comics/images" unless defined?(IMAGE_PATH)
11
+
12
+ namespace :webcomic do
13
+
14
+ desc "Creates a new Comic notes entry from image"
15
+ task :create do
16
+
17
+ require 'yaml'
18
+ require 'date'
19
+ Dir["#{IMAGE_PATH}/*.{jpg,jpe,jpeg,gif,png}"].each do |img_file|
20
+ img_filename = File.basename( img_file )
21
+ basename= img_filename.gsub( File.extname(img_filename), '' )
22
+ note_filename = "#{basename}.comic.markdown"
23
+ path= File.join NOTES_PATH, note_filename
24
+
25
+ unless File.exists? path
26
+ create_notes = ask("Notes for '#{img_filename}' are missing, create? [Yn] ")
27
+ if create_notes.upcase[0] != 'N'
28
+ meta= {
29
+ 'title' => basename,
30
+ 'slug' => basename,
31
+ 'publish_date' => Date.today,
32
+ 'filename' => img_filename
33
+ }.to_yaml
34
+
35
+ meta << "---\n\n"
36
+ meta << "\n\n"
37
+
38
+ File.open(path, "w") do |file|
39
+ file.write meta
40
+ end
41
+ puts "Created #{path}"
42
+ end
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ # desc "Validates that there are notes for each image"
49
+ # task :validate do
50
+ # ptus "Coming Soon!"
51
+ # end
52
+
53
+ end
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "middleman"
4
+ gem 'slim'
5
+ gem 'coffee-script'
6
+ gem 'builder'
7
+ gem "middleman-webcomic" #, :git=>"git@github.com:darthapo/middleman-webcomic.git"
@@ -0,0 +1,28 @@
1
+ # Settings:
2
+ NOTES_PATH= File.expand_path("./comics/notes")
3
+ IMAGE_PATH= File.expand_path("./comics/images")
4
+
5
+ # For the SYNC task
6
+ SERVER='USERNAME@YOURSITE.COM'
7
+ FOLDER="~/YOURSITE.COM"
8
+
9
+ require 'middleman-webcomic/tasks'
10
+
11
+ desc "Runs server"
12
+ task :server do
13
+ system "middleman server"
14
+ end
15
+
16
+ desc "Runs build"
17
+ task :build do
18
+ system "middleman build"
19
+ end
20
+
21
+ desc "Syncs with public server using rsync (if configured)"
22
+ task :sync=>:build do
23
+ cmd= "rsync -avz --delete build/ #{SERVER}:#{FOLDER}"
24
+ puts "Running:\n#{cmd}\n"
25
+ system(cmd)
26
+ puts "Done."
27
+ end
28
+
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+ require 'middleman'
5
+
6
+ run Middleman.server
7
+
8
+ # Rack config
9
+
10
+ # Look for index files in folders like Apache
11
+ # require "rack/contrib/try_static"
12
+ # use Rack::TryStatic, :root => "build", :urls => %w[/], :try => ['.html', 'index.html', '/index.html']
13
+
14
+ # Cache static assets
15
+ # require "rack/contrib/static_cache"
16
+ # use Rack::StaticCache, :urls => ['/'], :root => 'build'