This commit is contained in:
Jonathan Rudenberg 2012-08-21 23:15:29 -04:00
commit 72067ba038
19 changed files with 7758 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
tmp
output

16
Gemfile Normal file
View file

@ -0,0 +1,16 @@
source :rubygems
gem 'nanoc'
gem 'nanoc-cachebuster'
gem 'redcarpet'
gem 'slim'
gem 'rainpress'
gem 'nokogiri'
gem 'adsf'
gem 'builder'
gem 'mime-types'
gem 'rack'
gem 'unicorn'
gem 'rack-rewrite'
gem 'pygments.rb', :git => 'git://github.com/akzhan/pygments.rb.git', :branch => 'rubypython-0.6'
gem 'clogger'

64
Gemfile.lock Normal file
View file

@ -0,0 +1,64 @@
GIT
remote: git://github.com/akzhan/pygments.rb.git
revision: dfa0d936da40f25e249a7d20f895e6d4cab72b50
branch: rubypython-0.6
specs:
pygments.rb (0.2.12)
rubypython (~> 0.6.1)
GEM
remote: http://rubygems.org/
specs:
adsf (1.1.1)
rack (>= 1.0.0)
blankslate (2.1.2.4)
builder (3.0.0)
clogger (1.1.0)
rack (> 0.9)
colored (1.2)
cri (2.3.0)
colored (>= 1.2)
ffi (1.0.11)
kgio (2.7.4)
mime-types (1.19)
nanoc (3.4.0)
cri (~> 2.2)
nanoc-cachebuster (0.3.1)
nanoc (>= 3.3.0)
nokogiri (1.5.5)
rack (1.4.1)
rack-rewrite (1.2.1)
raindrops (0.10.0)
rainpress (1.0)
redcarpet (2.1.1)
rubypython (0.6.2)
blankslate (>= 2.1.2.3)
ffi (~> 1.0.7)
slim (1.2.2)
temple (~> 0.4.0)
tilt (~> 1.3.3)
temple (0.4.0)
tilt (1.3.3)
unicorn (4.3.1)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
PLATFORMS
ruby
DEPENDENCIES
adsf
builder
clogger
mime-types
nanoc
nanoc-cachebuster
nokogiri
pygments.rb!
rack
rack-rewrite
rainpress
redcarpet
slim
unicorn

76
Rules Normal file
View file

@ -0,0 +1,76 @@
#!/usr/bin/env ruby
preprocess do
create_robots_txt
create_webmaster_tools_authentications
create_sitemap
end
compile '/assets/css/*' do
filter :cache_buster
filter :rainpress
end
route %r{^/(assets/.*|sitemap|robots|atom)/$} do
next nil if item.identifier.split('/')[-1][0,1] == '_' # partial
fp = cachebust?(item) ? fingerprint(item[:filename]) : ''
item.identifier.chop + fp + '.' + item[:extension]
end
compile '/sitemap/' do
filter :erb
end
compile '/sitemap/', rep: 'gzip' do
filter :erb
filter :shellcmd, cmd: 'gzip'
end
route '/sitemap/', rep: 'gzip' do
'/sitemap.xml.gz'
end
compile '/posts/feed/' do
filter :erb
end
route '/posts/feed/' do
'/posts.xml'
end
compile %r{^/(google|robots|assets|favicon)} do
end
compile '*' do
case item[:extension]
when 'slim'
filter :slim
when 'md'
filter :redcarpet, renderer: MarkdownHTML, options: { fenced_code_blocks: true }
end
case item.identifier
when %r{^/posts/}
layout 'post'
else
layout 'default'
end
filter :cache_buster
end
route '/' do
'/index.html'
end
route '*' do
if item.binary?
# Write item with identifier /foo/ to /foo.ext
item.identifier.chop + '.' + item[:extension]
else
item.identifier.chop + '.html'
end
end
layout '*', :slim

78
config.ru Normal file
View file

@ -0,0 +1,78 @@
require 'bundler/setup'
require 'cgi'
require 'rack/static'
require 'rack/deflater'
require 'rack/rewrite'
require 'clogger'
ONE_YEAR = 31_556_926
class AssetExpires
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
headers.merge!('Cache-Control' => "max-age=#{ONE_YEAR}, public", 'Expires' => CGI.rfc1123_date(Time.now + ONE_YEAR)) if asset?(env)
[status, headers, body]
end
private
def asset?(env)
%w(js css jpg jpeg png gif).include?(env['PATH_INFO'].split('.').last) && env['PATH_INFO'] =~ /-cb/
end
end
class ContentTypeCharset
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
headers['Content-Type'] += ';charset=utf-8' unless headers['Content-Type'] =~ /charset/ || headers['Content-Type'].nil?
[status, headers, body]
end
end
# modified from https://github.com/meskyanichi/nanoc-heroku/blob/master/config.ru
class TryStatic < Rack::Static
def initialize(app, options)
super
@try = ([''] + Array(options.delete(:try)) + [''])
end
def call(env)
@next = 0
while @next < @try.size && 404 == (resp = super(try_next(env)))[0]
@next += 1
end
resp
end
private
def try_next(env)
env.merge('PATH_INFO' => env['PATH_INFO'] + @try[@next])
end
end
use Clogger, format: :NginxCombined, logger: STDOUT, reentrant: true if ENV['RACK_ENV'] == 'production'
use Rack::Rewrite do
r301 %r{.*}, 'http://tent.io$&', if: Proc.new { |env|
env['SERVER_NAME'] != 'tent.io' && !env['QUERY_STRING'].include?('skip_redirect')
} if ENV['RACK_ENV'] == 'production'
r301 %r{(.+)(/|\.html)$}, '$1'
end
use Rack::Deflater
use AssetExpires
use ContentTypeCharset
use TryStatic, root: 'output', urls: %w[/], try: %w(.html index.html /index.html)
run lambda { |env| [404, {}, ''] }

116
config.yaml Normal file
View file

@ -0,0 +1,116 @@
# A list of file extensions that nanoc will consider to be textual rather than
# binary. If an item with an extension not in this list is found, the file
# will be considered as binary.
text_extensions: [ 'coffee', 'css', 'erb', 'haml', 'handlebars', 'hb', 'htm', 'html', 'js', 'less', 'markdown', 'md', 'ms', 'mustache', 'php', 'rb', 'sass', 'scss', 'txt', 'xhtml', 'xml' ]
# The path to the directory where all generated files will be written to. This
# can be an absolute path starting with a slash, but it can also be path
# relative to the site directory.
output_dir: output
# A list of index filenames, i.e. names of files that will be served by a web
# server when a directory is requested. Usually, index files are named
# “index.html”, but depending on the web server, this may be something else,
# such as “default.htm”. This list is used by nanoc to generate pretty URLs.
index_filenames: [ 'index.html' ]
# Whether or not to generate a diff of the compiled content when compiling a
# site. The diff will contain the differences between the compiled content
# before and after the last site compilation.
enable_output_diff: false
prune:
# Whether to automatically remove files not managed by nanoc from the output
# directory. For safety reasons, this is turned off by default.
auto_prune: false
# Which files and directories you want to exclude from pruning. If you version
# your output directory, you should probably exclude VCS directories such as
# .git, .svn etc.
exclude: [ '.git', '.hg', '.svn', 'CVS' ]
# The data sources where nanoc loads its data from. This is an array of
# hashes; each array element represents a single data source. By default,
# there is only a single data source that reads data from the “content/” and
# “layout/” directories in the site directory.
data_sources:
-
# The type is the identifier of the data source. By default, this will be
# `filesystem_unified`.
type: filesystem_unified
# The path where items should be mounted (comparable to mount points in
# Unix-like systems). This is “/” by default, meaning that items will have
# “/” prefixed to their identifiers. If the items root were “/en/”
# instead, an item at content/about.html would have an identifier of
# “/en/about/” instead of just “/about/”.
items_root: /
# The path where layouts should be mounted. The layouts root behaves the
# same as the items root, but applies to layouts rather than items.
layouts_root: /
# Whether to allow periods in identifiers. When turned off, everything
# past the first period is considered to be the extension, and when
# turned on, only the characters past the last period are considered to
# be the extension. For example, a file named “content/about.html.erb”
# will have the identifier “/about/” when turned off, but when turned on
# it will become “/about.html/” instead.
allow_periods_in_identifiers: false
# Configuration for the “watch” command, which watches a site for changes and
# recompiles if necessary.
watcher:
# A list of directories to watch for changes. When editing this, make sure
# that the “output/” and “tmp/” directories are _not_ included in this list,
# because recompiling the site will cause these directories to change, which
# will cause the site to be recompiled, which will cause these directories
# to change, which will cause the site to be recompiled again, and so on.
dirs_to_watch: [ 'content', 'layouts', 'lib' ]
# A list of single files to watch for changes. As mentioned above, dont put
# any files from the “output/” or “tmp/” directories in here.
files_to_watch: [ 'config.yaml', 'Rules' ]
# When to send notifications (using Growl or notify-send).
notify_on_compilation_success: true
notify_on_compilation_failure: true
google_analytics_id: UA-34258323-1
base_url: http://tent.io
# Configure the robots.txt file for this site.
# Setting 'default' to true-ish will use sensible defaults. If you
# wish to customize it, you can list paths to allow and to disallow.
# Finally, you could manually set the path to the sitemap file.
#
# You can customize the robots file fairly well like this, but you
# can always manually create a content file with the exact contents
# you need.
robots:
default: true # point at sitemap
# disallow:
# - '/tag'
# - '/newsletter'
# allow:
# - '/tag/foo'
# sitemap: '/site-map.txt'
# Set up authentication files for various webmaster tools (or something
# similar). This simply creates a plain text file when generating the site.
#
# identifier: identifier of the output file, e.g. '/google12345/'
# content: content of the file, e.g. 'aoa8202ns001'
# extension: extension of the output file, e.g. 'html' or 'xml'
webmaster_tools:
-
identifier:
extension:
content:
# Should Nanoc generate some standard asset files for you, or skip them
# completely?
#
# This currently controls the generation of robots.txt, sitemap.xml,
# sitemap.xml.gz and webmaster tools authentication files.
output_generated_assets: true

File diff suppressed because it is too large Load diff

5624
content/assets/css/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,61 @@
.hll { background-color: #ffffcc }
.c { color: #408080; font-style: italic } /* Comment */
.err { border: 1px solid #FF0000 } /* Error */
.k { color: #008000; font-weight: bold } /* Keyword */
.o { color: #666666 } /* Operator */
.cm { color: #408080; font-style: italic } /* Comment.Multiline */
.cp { color: #BC7A00 } /* Comment.Preproc */
.c1 { color: #408080; font-style: italic } /* Comment.Single */
.cs { color: #408080; font-style: italic } /* Comment.Special */
.gd { color: #A00000 } /* Generic.Deleted */
.ge { font-style: italic } /* Generic.Emph */
.gr { color: #FF0000 } /* Generic.Error */
.gh { color: #000080; font-weight: bold } /* Generic.Heading */
.gi { color: #00A000 } /* Generic.Inserted */
.go { color: #808080 } /* Generic.Output */
.gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.gs { font-weight: bold } /* Generic.Strong */
.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.gt { color: #0040D0 } /* Generic.Traceback */
.kc { color: #008000; font-weight: bold } /* Keyword.Constant */
.kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
.kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
.kp { color: #008000 } /* Keyword.Pseudo */
.kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
.kt { color: #B00040 } /* Keyword.Type */
.m { color: #666666 } /* Literal.Number */
.s { color: #BA2121 } /* Literal.String */
.na { color: #7D9029 } /* Name.Attribute */
.nb { color: #008000 } /* Name.Builtin */
.nc { color: #0000FF; font-weight: bold } /* Name.Class */
.no { color: #880000 } /* Name.Constant */
.nd { color: #AA22FF } /* Name.Decorator */
.ni { color: #999999; font-weight: bold } /* Name.Entity */
.ne { color: #D2413A; font-weight: bold } /* Name.Exception */
.nf { color: #0000FF } /* Name.Function */
.nl { color: #A0A000 } /* Name.Label */
.nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
.nt { color: #008000; font-weight: bold } /* Name.Tag */
.nv { color: #19177C } /* Name.Variable */
.ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
.w { color: #bbbbbb } /* Text.Whitespace */
.mf { color: #666666 } /* Literal.Number.Float */
.mh { color: #666666 } /* Literal.Number.Hex */
.mi { color: #666666 } /* Literal.Number.Integer */
.mo { color: #666666 } /* Literal.Number.Oct */
.sb { color: #BA2121 } /* Literal.String.Backtick */
.sc { color: #BA2121 } /* Literal.String.Char */
.sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
.s2 { color: #BA2121 } /* Literal.String.Double */
.se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
.sh { color: #BA2121 } /* Literal.String.Heredoc */
.si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
.sx { color: #008000 } /* Literal.String.Other */
.sr { color: #BB6688 } /* Literal.String.Regex */
.s1 { color: #BA2121 } /* Literal.String.Single */
.ss { color: #19177C } /* Literal.String.Symbol */
.bp { color: #008000 } /* Name.Builtin.Pseudo */
.vc { color: #19177C } /* Name.Variable.Class */
.vg { color: #19177C } /* Name.Variable.Global */
.vi { color: #19177C } /* Name.Variable.Instance */
.il { color: #666666 } /* Literal.Number.Integer.Long */

422
content/docs/app-auth.md Normal file
View file

@ -0,0 +1,422 @@
---
title: App Authentication
---
# App Authentication
Tent uses [OAuth 2](http://tools.ietf.org/html/draft-ietf-oauth-v2-31) for app
authentication. Because of the distributed nature of Tent, it is necessary for
apps to register with the Tent entity before doing the authentication flow.
After the authentication flow, requests are authenticated using the credentials
with [MAC Access
Authentication](http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01).
## App Registration
Before authenticating a user, the application must be registered with the
specified Tent entity. The first step is to perform discovery on the provided
entity url.
```text
HEAD /
Host: titanous.com
```
```text
200 OK
Tent-Server: https://tent.titanous.com
```
The next step is to register the application with the server. Registration
specifies some details about the app, as well as the exact `redirect_uris` that
will be used during the OAuth flow. The response `id`, `secret`, and
`mac_algorithm` are used to authenticate future requests to the Tent server.
The `id` is used as the `client_id` in all OAuth requests.
```text
POST /apps
Content-Type: application/json
Accept: application/json
```
```json
{
"name": "FooApp",
"description": "Does amazing foos with your data",
"url": "http://foooo.com",
"icon": "http://foooo.com/icon.png",
"redirect_uris": ["http://fooapp.com/tent/callback"],
"scopes": {
"write_profile": "Uses an app profile section to describe foos",
"read_followings": "Calculates foos based on your followings"
}
}
```
```text
201 Created
Content-Type: application/json
Location: https://tent.titanous.com/apps/6737b
```
```json
{
"id": "6737b",
"secret": "3d2adf9a68bf64f4eaff70a7c7700a8",
"mac_algorithm": "hmac-sha-256"
}
```
### Request Parameters
<table>
<thead>
<tr>
<th>Name</th>
<th>Required</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>name</code></td>
<td>Required</td>
<td>String</td>
<td>The human name of the app to show the user</td>
</tr>
<tr>
<td><code>description</code></td>
<td>Required</td>
<td>String</td>
<td>A short description of the application to show the user</td>
</tr>
<tr>
<td><code>url</code></td>
<td>Required</td>
<td>String</td>
<td>The main url of the app</td>
</tr>
<tr>
<td><code>icon</code></td>
<td>Optional</td>
<td>String</td>
<td>The url to an icon for the app</td>
</tr>
<tr>
<td><code>redirect_uris</code></td>
<td>Optional</td>
<td>Array</td>
<td>
A list of <strong>exact</strong> (including parameters) urls that will
be used as OAuth <code>redirect_uri</code>.
</td>
</tr>
<tr>
<td><code>scopes</code></td>
<td>Optional</td>
<td>Object</td>
<td>
A list of scope key to description value mappings of all scopes that
the app might use. The descriptions should describe why the specific
scope is necessary for the app to function.
</td>
</tr>
</tbody>
</table>
### Response Parameters
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>id</code></td>
<td>
The identifier of the app. This is used as the MAC key identifier for
requests to/from the Tent server, as well as the <code>client_id</code>
in the OAuth flow.
</td>
</tr>
<tr>
<td><code>secret</code></td>
<td>
The secret used as the MAC key when modifying the registration and
receiving notifications.
</td>
</tr>
<tr>
<td><code>mac_algorithm</code></td>
<td>The MAC algorithm to be used.</td>
</tr>
</tbody>
</table>
## App Registration Modification
The request must be authenticated with a MAC generated using the secret from the
initial registration.
### PATCH /apps/:id
```text
PATCH /apps/aff70a7
Host: tent.titanous.com
Content-Type: application/json
Accept: application/json
Authorization: MAC id="aff70a7",
ts="1336363200",
nonce="dj83hs9s",
mac="bhCQXTVyfj5cmA9uKkPFx1zeOXM="
```
```json
{
"name": "FooApp",
"description": "Does amazing foos with your data",
"url": "http://foooo.com",
"icon": "http://foooo.com/icon.png",
"redirect_uris": ["http://fooapp.com/tent/callback"],
"scopes": {
"write_profile": "Uses an app profile section to describe foos",
"read_followings": "Calculates foos based on your followings"
}
}
```
## Authentication Flow
### Auth Request
The app requests the user's Tent identifier, and performs discovery on it to
find the Tent API root. The app then builds an auth request and redirects the
user-agent to it:
```text
/auth?client_id=6737b
&redirect_uri=http://fooapp.com/tent/callback
&scope=read_posts,read_profile
&state=87351cc2f6737bfc8ba
&tent_profile_info_types=https://tent.io/types/info/music
&tent_post_types=https://tent.io/types/posts/status#0.2.0,https://tent.io/types/posts/photo#0.2.0
```
#### Parameters
<table>
<thead>
<tr>
<th>Name</th>
<th>Required</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>client_id</code></td>
<td>Required</td>
<td>
The <code>id</code> obtained by registering with the Tent server
</td>
</tr>
<tr>
<td><code>redirect_uri</code></td>
<td>Required</td>
<td>
The URI to redirect to after authentication is complete. It must
<strong>exactly</strong> match a URI (including parameters)
provided during app registration in <code>redirect_uris</code>.
</td>
</tr>
<tr>
<td><code>state</code></td>
<td>Optional</td>
<td>
This parameter will be added to the <code>redirect_uri</code> and should
always be set to a random string that is stored in the session, and then
verified to prevent cross-site request forgery attacks.
</td>
</tr>
<tr>
<td><code>scope</code></td>
<td>Optional</td>
<td>
A comma-separated list of scopes that the app is requesting access to.
</td>
</tr>
<tr>
<td><code>tent_profile_info_types</code></td>
<td>Optional</td>
<td>
A comma-separated list of <code>profile_info_type_url#version</code>
profile info type specifiers that the app is requesting access to. Set
to <code>all</code> to request full access to the profile.
</td>
</tr>
<tr>
<td><code>tent_post_types</code></td>
<td>Optional</td>
<td>
A comma-separated list of <code>post_type_url#version</code>
type/version specifiers that the app is requesting access to.
Set to <code>all</code> to request access to all posts.
</td>
</tr>
</tbody>
</table>
#### Scopes
<table>
<thead>
<tr>
<th>Scope</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>read_profile</code></td>
<td>Read profile sections listed in the <code>profile_info</code> parameter</td>
</tr>
<tr>
<td><code>write_profile</code></td>
<td>Read and write profile sections listed in the <code>profile_info</code> parameter</td>
</tr>
<tr>
<td><code>read_followers</code></td>
<td>Read follower list</td>
</tr>
<tr>
<td><code>write_followers</code></td>
<td>Read follower list and block followers</td>
</tr>
<tr>
<td><code>read_followings</code></td>
<td>Read followings list</td>
</tr>
<tr>
<td><code>write_followings</code></td>
<td>Read followings list and follow new entities</td>
</tr>
<tr>
<td><code>read_posts</code></td>
<td>Read posts with types listed in the <code>post_types</code> parameter</td>
</tr>
<tr>
<td><code>write_posts</code></td>
<td>Read and publish posts with types listed in the <code>post_types</code> parameter</td>
</tr>
</tbody>
</table>
### Redirect
After the user has authorized the application, the Tent server will redirect the
User-Agent back to the specified `redirect_uri` with a `code` that can be used
to retrieve authentication details from the server.
The `state` parameter should be matched against the `state` parameter sent in
the initial request to prevent request forgery.
```text
302 Found
Location: http://fooapp.com/tent/callback?code=27ec1c54980f1af74&state=87351cc2f6737bfc8ba
```
### Access Token
The `code` must be traded for permanent authentication details that can be used
to access the Tent server on behalf of the user.
#### POST /apps/:id/access\_token
The request must be signed with a MAC using the secret obtained during app
registration. Currently only the `mac` `token_type` is supported.
```text
POST /apps/6737b/access_token
Content-Type: application/json
Accept: application/json
Authorization: MAC id="6737b",
ts="1336363200",
nonce="dj83hs9s",
mac="bhCQXTVyfj5cmA9uKkPFx1zeOXM="
```
```json
{
"code": "27ec1c54980f1af74",
"token_type": "mac"
}
```
The server will respond with the authentication details necessary to interact
with the Tent server on behalf of the user.
```text
200 OK
Content-Type: application/json
```
```json
{
"access_token": "7b7df67598602",
"mac_key": "18bcf2bae86948731",
"mac_algorithm": "hmac-sha-256"
"token_type": "mac",
}
```
#### Response Parameters
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>access_token</code></td>
<td>Used as the MAC key identifier.</td>
</tr>
<tr>
<td><code>mac_key</code></td>
<td>Used as the MAC key for requests.</td>
</tr>
<tr>
<td><code>mac_algorithm</code></td>
<td>The MAC algorithm to be used.</td>
</tr>
<tr>
<td><code>token_type</code></td>
<td>Specifies the token type. Currently always <code>mac</code></td>
</tr>
</tbody>
</table>
## Request Authentication
Tent uses [HTTP MAC Access
Authentication](http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01) to
authenticate app requests. Requests to modify the app's registration details
must be authorized using the provided `id` and `secret` as the
MAC key identifier and MAC key respectively.
Requests to the Tent server on behalf of an authenticated user must be
authorized using the credentials retrieved at the end of the auth flow.
The `access_token` and `mac_key` are the MAC key identifier and MAC key
respectively.
Currently all Authorization headers in this documentation are fake, but they
will be real examples when we have the example generator written.

14
content/index.html Normal file
View file

@ -0,0 +1,14 @@
---
title: Home
---
<h1>A Brand New nanoc Site</h1>
<p>Youve just created a new nanoc site. The page you are looking at right now is the home page for your site. To get started, consider replacing this default homepage with your own customized homepage. Some pointers on how to do so:</p>
<ul>
<li><p><strong>Change this pages content</strong> by editing the “index.html” file in the “content” directory. This is the actual page content, and therefore doesnt include the header, sidebar or style information (those are part of the layout).</p></li>
<li><p><strong>Change the layout</strong>, which is the “default.html” file in the “layouts” directory, and create something unique (and hopefully less bland).</p></li>
</ul>
<p>If you need any help with customizing your nanoc web site, be sure to check out the documentation (see sidebar), and be sure to subscribe to the discussion group (also see sidebar). Enjoy!</p>

6
layouts/default.slim Normal file
View file

@ -0,0 +1,6 @@
doctype html
html lang='en'
head == render 'head'
body
#container
== yield

17
layouts/head.slim Normal file
View file

@ -0,0 +1,17 @@
meta name="viewport" content='width=device-width, initial-scale=1.0'
title = "#{@item[:title] + ' · ' if @item[:title]}Tent"
link href='/assets/css/bootstrap.css' rel='stylesheet' type='text/css' media='screen'
link href='/assets/css/bootstrap-responsive.css' rel='stylesheet' type='text/css' media='screen'
link href='/assets/css/style.css' rel='stylesheet' type='text/css' media='screen'
link rel='alternate' type='application/atom+xml' title='Atom feed' href="#{@site.config[:base_url]}/posts.xml"
javascript:
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '#{@site.config[:google_analytics_id]}']);
_gaq.push(['_setSiteSpeedSampleRate', 50]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();

8
lib/blogging.rb Normal file
View file

@ -0,0 +1,8 @@
module Nanoc3::Helpers::Blogging
# Patch to add analytics param to feed urls
alias url_for_without_analytics url_for
def url_for(item)
url = url_for_without_analytics(item)
url.sub(/(\.html|\/)$/, '') + '?utm_source=feed'
end
end

12
lib/default.rb Normal file
View file

@ -0,0 +1,12 @@
require 'nanoc/cachebuster'
require 'slim'
require 'redcarpet'
include Nanoc3::Helpers::Rendering
include Nanoc3::Helpers::XMLSitemap
include Nanoc3::Helpers::Blogging
include Nanoc3::Helpers::Filtering
include Nanoc3::Helpers::LinkTo
include Nanoc3::Helpers::CacheBusting
Slim::Engine.set_default_options pretty: true

9
lib/markdown_html.rb Normal file
View file

@ -0,0 +1,9 @@
require 'pygments.rb'
RubyPython.start python_exe: 'python2.6' if ENV['RACK_ENV'] == 'production'
class MarkdownHTML < Redcarpet::Render::HTML
def block_code(code, language)
Pygments.highlight(code, lexer: language)
end
end

86
lib/meta.rb Normal file
View file

@ -0,0 +1,86 @@
# Source: https://github.com/avdgaag/nanoc-template/blob/master/lib/preprocessors.rb
# Generate a sitemap.xml file using Nanoc's own xml_sitemap helper method by
# dynamically adding a new item.
#
# Make items that should not appear in the sitemap hidden. This by default
# works on all image files and typical assets, as well as error pages and
# htaccess. The is_hidden attribute is only explicitly set if it is absent,
# allowing per-file overriding.
#
# @todo extract hidden file types into configuration file?
def create_sitemap
return unless @site.config[:output_generated_assets]
@items.each do |item|
if %w{png gif jpg jpeg coffee scss sass less css xml js txt ico}.include?(item[:extension]) ||
item.identifier =~ /404|500|htaccess/
item[:is_hidden] = true unless item.attributes.has_key?(:is_hidden)
end
end
@items << Nanoc3::Item.new(
"<%= xml_sitemap %>",
{ :extension => 'xml', :is_hidden => true },
'/sitemap/'
)
end
# Use special settings from the site configuration to generate the files
# necessary for various webmaster tools authentications, such as the services
# from Google, Yahoo and Bing.
#
# This loops through all the items in the `webmaster_tools` setting, using
# its properties to generate a new item.
#
# See config.yaml for more documentation on the input format.
def create_webmaster_tools_authentications
return unless @site.config[:output_generated_assets]
@site.config[:webmaster_tools].each do |file|
next if file[:identifier].nil?
content = file.delete(:content)
identifier = file.delete(:identifier)
file.merge!(:is_hidden => true)
@items << Nanoc3::Item.new(
content,
file,
identifier
)
end
end
# Generate a robots.txt file in the root of the site by dynamically creating
# a new item.
#
# This will either output a default robots.txt file, that disallows all
# assets except images, and points to the sitemap file.
#
# You can override the contents of the output of this method using the site
# configuration, specifying Allow and Disallow directives. See the config.yaml
# file for more information on the expected input format.
def create_robots_txt
return unless @site.config[:output_generated_assets]
if @site.config[:robots]
content = if @site.config[:robots][:default]
<<-EOS
User-agent: *
Allow: *
Sitemap: #{@site.config[:base_url]}/sitemap.xml
EOS
else
[
'User-Agent: *',
@site.config[:robots][:disallow].map { |l| "Disallow: #{l}" },
(@site.config[:robots][:allow] || []).map { |l| "Allow: #{l}" },
"Sitemap: #{@site.config[:robots][:sitemap]}"
].flatten.compact.join("\n")
end
@items << Nanoc3::Item.new(
content,
{ :extension => 'txt', :is_hidden => true },
'/robots/'
)
end
end

31
lib/shellcmd.rb Normal file
View file

@ -0,0 +1,31 @@
require 'open3'
# This nanoc filter is a general purpose filter that simply pipes
# the contents of an item into a given shell command, and sets
# the items output to the output of it.
#
# It is NOT safe to use on large inputs, which will cause I/O
# deadlocks. Any safer implementation is encouraged.
#
# Usage:
#
# compile '/static/js/*/' do
# # minify JS :)
# filter :shellcmd, "java -jar js-compiler.jar"
# end
#
# Written by Vincent Driessen (http://twitter.com/nvie) and
# released to the public domain.
#
# http://nvie.com
class ShellCmdFilter < Nanoc3::Filter
identifier :shellcmd
def run(content, params)
Open3.popen3(params[:cmd]) do |stdin, stdout, stderr|
stdin.write(content)
stdin.close
stdout.read
end
end
end

76
lib/xml_sitemap.rb Normal file
View file

@ -0,0 +1,76 @@
# Source: https://github.com/ddfreyne/nanoc/blob/master/lib/nanoc/helpers/xml_sitemap.rb
# hacked to remove trailing slash
module Nanoc::Helpers
# Contains functionality for building XML sitemaps that will be crawled by
# search engines. See the [Sitemaps protocol site](http://www.sitemaps.org)
# for details.
module XMLSitemap
# Builds an XML sitemap and returns it.
#
# The following attributes can optionally be set on items to change the
# behaviour of the sitemap:
#
# * `changefreq` — The estimated change frequency as defined by the
# Sitemaps protocol
#
# * `priority` — The item's priority, ranging from 0.0 to 1.0, as defined
# by the Sitemaps protocol
#
# The sitemap will also include dates on which the items were updated.
# These are generated automatically; the way this happens depends on the
# used data source (the filesystem data source checks the file mtimes, for
# instance).
#
# The site configuration will need to have the following attributes:
#
# * `base_url` — The URL to the site, without trailing slash. For example,
# if the site is at "http://example.com/", the `base_url` would be
# "http://example.com".
#
# @example Excluding binary items from the sitemap
#
# <%= xml_sitemap :items => @items.reject{ |i| i[:is_hidden] || i.binary? } %>
#
# @option params [Array] :items A list of items to include in the sitemap
#
# @return [String] The XML sitemap
def xml_sitemap(params={})
require 'builder'
# Extract parameters
items = params[:items] || @items.reject { |i| i[:is_hidden] }
# Create builder
buffer = ''
xml = Builder::XmlMarkup.new(:target => buffer, :indent => 2)
# Check for required attributes
if @site.config[:base_url].nil?
raise RuntimeError.new("The Nanoc::Helpers::XMLSitemap helper requires the site configuration to specify the base URL for the site.")
end
# Build sitemap
xml.instruct!
xml.urlset(:xmlns => 'http://www.sitemaps.org/schemas/sitemap/0.9') do
# Add item
items.each do |item|
item.reps.reject { |r| r.raw_path.nil? }.each do |rep|
xml.url do
xml.loc @site.config[:base_url] + rep.path.sub(/(\.html|\/)$/, '')
xml.lastmod item.mtime.to_iso8601_date unless item.mtime.nil?
xml.changefreq item[:changefreq] unless item[:changefreq].nil?
xml.priority item[:priority] unless item[:priority].nil?
end
end
end
end
# Return sitemap
buffer
end
end
end