writecast 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/TODO +44 -0
- data/bin/writecast +28 -0
- data/lib/writecast.rb +7 -0
- data/lib/writecast/commands/help.rb +52 -0
- data/lib/writecast/commands/init.rb +14 -0
- data/lib/writecast/commands/override.rb +14 -0
- data/lib/writecast/commands/process.rb +106 -0
- data/lib/writecast/commands/serve.rb +31 -0
- data/lib/writecast/config.rb +13 -0
- data/lib/writecast/models.rb +233 -0
- data/lib/writecast/version.rb +3 -0
- data/lib/writecast/views/all_tags_index.rb +24 -0
- data/lib/writecast/views/base.rb +90 -0
- data/lib/writecast/views/index.rb +51 -0
- data/lib/writecast/views/month_index.rb +22 -0
- data/lib/writecast/views/page.rb +18 -0
- data/lib/writecast/views/post.rb +63 -0
- data/lib/writecast/views/tag_index.rb +26 -0
- data/lib/writecast/views/year_index.rb +21 -0
- data/writecast.gemspec +29 -0
- metadata +203 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/TODO
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
Write some docs with examples - e.g. how to run server locally, recommended workflow etc.
|
2
|
+
Add comment form to posts and pages (as another GEM ?)
|
3
|
+
\-Add rack script to email on comments posted to it from a form in the page.
|
4
|
+
Script will simply respond to HTTP POST requests forwarded: nginx ---*socket*---> unicorn -> script "/comment" (also '/contact'?)
|
5
|
+
Fix views to use Erector::Widget::Page, and lose the 'rawtext's
|
6
|
+
Put a copy of jquery + friends(form,validate), and a simple stylesheet in an static dir, which is copied out on init.
|
7
|
+
Try to use CSS animations rather than a GIF for comment loading.
|
8
|
+
|
9
|
+
Much, much later:
|
10
|
+
Add Alt text to links in tag cloud - list how many articles.
|
11
|
+
Consider Writecast::separate() which takes an array of strings and returns a single string with the SEPARATOR inserted between arguments. It may not be worth it, see models.rb.
|
12
|
+
Allow multi-level pages.
|
13
|
+
Move to_atom (and any other common functionality) in models.rb to a mixin.
|
14
|
+
Clean up views/base.rb render_menu logic.
|
15
|
+
Search all code for magic values - add these to the config!
|
16
|
+
Tests?
|
17
|
+
\-Try inserting images with names including ']'
|
18
|
+
|
19
|
+
Done:
|
20
|
+
Show recent posts on front page.
|
21
|
+
Show summaries on month indices.
|
22
|
+
Display time information on posts.
|
23
|
+
Handle images in filename_static
|
24
|
+
Fix images to work correctly on index page.
|
25
|
+
Handle posts+pages with no yaml header.
|
26
|
+
Display tags correctly on posts, and in tag cloud on front page.
|
27
|
+
Add date to Pages.
|
28
|
+
Fix URLs with proper encoding.
|
29
|
+
Put configuration info in config.rb
|
30
|
+
Use Writecast:: namespace.
|
31
|
+
Code syntax highlighting.
|
32
|
+
document.rb Index.to_html TODO
|
33
|
+
Reconsider the layout of the site. It would be nice to have less 'tagged-*' folders in root.
|
34
|
+
URL encode tag directories.
|
35
|
+
Create /init directory to copy files for initial site setup.
|
36
|
+
Make app use rack interface.
|
37
|
+
Allow use of unicode filenames and titles for posts and pages.
|
38
|
+
Integrate server into program? -- this is handled by thin now.
|
39
|
+
Package as Gem.
|
40
|
+
Write init function which will set up given folder for site.
|
41
|
+
Add homepage (github) to gemspec.
|
42
|
+
Generate an RSS feed for each tag and for the entire index.
|
43
|
+
Allow overriding page, post, index views. do check in views/base and add command "override 'views/whatever'".
|
44
|
+
Add a font to CSS?
|
data/bin/writecast
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# load the local config file if any
|
4
|
+
require_relative File.join(Dir.pwd, 'config.rb') if File.exist? 'config.rb'
|
5
|
+
require_relative '../lib/writecast'
|
6
|
+
|
7
|
+
def Writecast::abort
|
8
|
+
Kernel::abort "Invalid command. Try 'writecast help' for usage info."
|
9
|
+
end
|
10
|
+
|
11
|
+
if (1..2).include? ARGV.length
|
12
|
+
case ARGV[0]
|
13
|
+
when 'help'
|
14
|
+
Writecast::help
|
15
|
+
when 'init'
|
16
|
+
Writecast::init ARGV[1] || Dir.pwd
|
17
|
+
when 'process'
|
18
|
+
Writecast::process ARGV[1] || Dir.pwd
|
19
|
+
when 'serve'
|
20
|
+
Writecast::serve ARGV[1] || Dir.pwd
|
21
|
+
when 'override'
|
22
|
+
Writecast::override ARGV[1]
|
23
|
+
else
|
24
|
+
Writecast::abort
|
25
|
+
end
|
26
|
+
else
|
27
|
+
Writecast::abort
|
28
|
+
end
|
data/lib/writecast.rb
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# each command is a different process which will require its own libraries
|
2
|
+
require_relative 'writecast/commands/process'
|
3
|
+
require_relative 'writecast/commands/init'
|
4
|
+
require_relative 'writecast/commands/serve'
|
5
|
+
require_relative 'writecast/commands/help'
|
6
|
+
require_relative 'writecast/commands/override'
|
7
|
+
require_relative 'writecast/version'
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'active_support/core_ext/string'
|
3
|
+
|
4
|
+
def Writecast::help
|
5
|
+
|
6
|
+
def em string
|
7
|
+
"\033[1m#{string}\033[m"
|
8
|
+
end
|
9
|
+
|
10
|
+
puts <<-DOC.strip_heredoc
|
11
|
+
|
12
|
+
#{em'NAME'}
|
13
|
+
writecast - a slightly opinionated static website generator.
|
14
|
+
|
15
|
+
#{em'SYNOPSIS'}
|
16
|
+
#{em'writecast help'}
|
17
|
+
#{em'writecast init'} [target_directory]
|
18
|
+
#{em'writecast process'} [target_directory]
|
19
|
+
#{em'writecast serve'} [target_directory]
|
20
|
+
#{em'writecast override'} target_view
|
21
|
+
|
22
|
+
#{em'DESCRIPTION'}
|
23
|
+
#{em'writecast'} is a static website generator. It is slightly
|
24
|
+
'opinionated' in that it uses a few conventions in order to make
|
25
|
+
publishing a simple website easy.
|
26
|
+
|
27
|
+
#{em'EXAMPLES'}
|
28
|
+
#{em'writecast help'}
|
29
|
+
Display this help documentation.
|
30
|
+
|
31
|
+
#{em'writecast init'} ~/my_new_blog
|
32
|
+
Initialise the directory structure below '~/my_new_blog'.
|
33
|
+
|
34
|
+
#{em'writecast process'} ~/my_new_blog
|
35
|
+
...
|
36
|
+
#{em'writecast serve'} ~/my_new_blog
|
37
|
+
...
|
38
|
+
#{em'writecast override'} views/index
|
39
|
+
...
|
40
|
+
|
41
|
+
#{em'OVERVIEW'}
|
42
|
+
|
43
|
+
#{em'DEFAULTS'}
|
44
|
+
|
45
|
+
#{em'ERROR REPORTING'}
|
46
|
+
|
47
|
+
#{em'EXIT STATUS'}
|
48
|
+
|
49
|
+
#{em'HISTORY'}
|
50
|
+
|
51
|
+
DOC
|
52
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
def Writecast::init destination
|
2
|
+
Kernel.abort "Destination must be a directory" unless File.ftype(destination) == "directory"
|
3
|
+
puts "Initialising site in '#{destination}'"
|
4
|
+
p Dir.pwd
|
5
|
+
puts "creating 'content/pages'"
|
6
|
+
FileUtils.mkdir_p(File.join(destination, 'content', 'pages'))
|
7
|
+
puts "creating 'content/posts'"
|
8
|
+
FileUtils.mkdir_p(File.join(destination, 'content', 'posts'))
|
9
|
+
puts "creating 'content/static'"
|
10
|
+
FileUtils.mkdir_p(File.join(destination, 'content', 'static'))
|
11
|
+
puts "creating 'public_html/'"
|
12
|
+
FileUtils.mkdir_p(File.join(destination, 'public_html'))
|
13
|
+
puts "done."
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
def Writecast::override view_path
|
2
|
+
file_path = File.join(File.dirname(__FILE__), '..', "#{view_path}.rb")
|
3
|
+
if File.exist? file_path
|
4
|
+
local_file_path = File.join(Dir.pwd, "#{view_path}.rb")
|
5
|
+
if File.exist? local_file_path
|
6
|
+
Kernel.abort 'view already overridden'
|
7
|
+
else
|
8
|
+
FileUtils.mkdir_p File.join(Dir.pwd, 'views')
|
9
|
+
FileUtils.cp(file_path, local_file_path)
|
10
|
+
end
|
11
|
+
else
|
12
|
+
Kernel.abort 'no such view'
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require_relative '../config'
|
2
|
+
require_relative '../models'
|
3
|
+
|
4
|
+
def Writecast::process dir
|
5
|
+
|
6
|
+
Dir::chdir dir
|
7
|
+
|
8
|
+
FileUtils.rm_r(Dir["public_html/*"]) # clean old public_html
|
9
|
+
FileUtils.mkdir("public_html") unless Dir.exist?("public_html")
|
10
|
+
FileUtils.cp_r('content/static', 'public_html/static') # set up new public_html's static content
|
11
|
+
|
12
|
+
# store posts in a nested hash by date, and in a hash by tag
|
13
|
+
posts_hash = Hash.new{|h,k| h[k]=Hash.new(&h.default_proc)}
|
14
|
+
tags_hash = {}
|
15
|
+
num_posts = 0
|
16
|
+
Dir["content/posts/**/*"].each do |post_raw|
|
17
|
+
if File.ftype(post_raw) == 'file' && Writecast::DOC_EXTS.include?(File.extname(post_raw).downcase)
|
18
|
+
post = Writecast::Post.new(post_raw)
|
19
|
+
|
20
|
+
if posts_hash[post.time.year][post.time.month].empty?
|
21
|
+
posts_hash[post.time.year][post.time.month] = [post]
|
22
|
+
else
|
23
|
+
posts_hash[post.time.year][post.time.month] << post
|
24
|
+
end
|
25
|
+
|
26
|
+
post.tags and post.tags.each do |tag|
|
27
|
+
escaped_tag = CGI.escape(tag)
|
28
|
+
if tags_hash[[tag,escaped_tag]]
|
29
|
+
tags_hash[[tag,escaped_tag]] << post
|
30
|
+
else
|
31
|
+
tags_hash[[tag,escaped_tag]] = [post]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# create public_html folder structure
|
36
|
+
post_path = File.dirname(post.file_path)
|
37
|
+
FileUtils.mkdir_p("public_html#{post_path}")
|
38
|
+
|
39
|
+
# copy static files associated with post, if any
|
40
|
+
if Dir.exist?("#{post_raw}_static")
|
41
|
+
FileUtils.cp_r(
|
42
|
+
"#{post_raw}_static",
|
43
|
+
"public_html#{post_path}/#{File.basename(post_raw)}_static"
|
44
|
+
)
|
45
|
+
end
|
46
|
+
num_posts += 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# posts_hash is passed to the index so that recent posts and yearly archives can be built.
|
51
|
+
index = Writecast::Index.new(posts_hash, tags_hash, num_posts)
|
52
|
+
# generate index atom feed
|
53
|
+
File.open("public_html/feed.atom", "w") {|file| file.write(index.to_atom)}
|
54
|
+
|
55
|
+
pages = [index]
|
56
|
+
Dir["content/pages/**/*"].each do |page_raw|
|
57
|
+
if File.ftype(page_raw) == 'file' && Writecast::DOC_EXTS.include?(File.extname(page_raw).downcase)
|
58
|
+
page = Writecast::Page.new(page_raw)
|
59
|
+
pages << page
|
60
|
+
|
61
|
+
page_path = File.dirname(page.file_path)
|
62
|
+
if Dir.exist?("#{page_raw}_static")
|
63
|
+
FileUtils.cp_r(
|
64
|
+
"#{page_raw}_static",
|
65
|
+
"public_html#{page_path}/#{File.basename(page_raw)}_static"
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
pages.each do |page|
|
71
|
+
File.open("public_html/#{page.file_path}", "w") {|file| file.write(page.to_html(pages))}
|
72
|
+
end
|
73
|
+
|
74
|
+
posts_hash.each do |year,month_hash|
|
75
|
+
|
76
|
+
# write year indices
|
77
|
+
File.open("public_html/#{year}/index.html", "w") do |file|
|
78
|
+
file.write(Writecast::Year_index.new(year, month_hash).to_html(pages))
|
79
|
+
end
|
80
|
+
|
81
|
+
month_hash.each do |month,month_posts|
|
82
|
+
# write month indices
|
83
|
+
File.open("public_html/#{year}/#{"%02d" % month}/index.html", "w") do |file|
|
84
|
+
file.write(Writecast::Month_index.new(year, month, month_posts).to_html(pages))
|
85
|
+
end
|
86
|
+
|
87
|
+
# write posts
|
88
|
+
month_posts.each do |post|
|
89
|
+
File.open("public_html/#{post.file_path}", "w") {|file| file.write(post.to_html(pages))}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
FileUtils.mkdir_p("public_html/tags")
|
95
|
+
tags_hash.each do |tag_set, posts|
|
96
|
+
tag,escaped_tag = tag_set
|
97
|
+
FileUtils.mkdir_p("public_html/tags/#{tag}")
|
98
|
+
tag_index = Writecast::Tag_index.new(tag_set, posts)
|
99
|
+
File.open("public_html/tags/#{tag}/index.html", "w") {|file| file.write(tag_index.to_html(pages))}
|
100
|
+
# generate tag atom feed
|
101
|
+
File.open("public_html/tags/#{tag}/feed.atom", "w") {|file| file.write(tag_index.to_atom)}
|
102
|
+
end
|
103
|
+
File.open("public_html/tags/index.html", "w") do |file|
|
104
|
+
file.write(Writecast::All_tags_index.new(tags_hash).to_html(pages))
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
def Writecast::serve dir
|
4
|
+
|
5
|
+
rack_app = Proc.new { |env|
|
6
|
+
|
7
|
+
root = File.join(dir, 'public_html')
|
8
|
+
default_charset = "; charset=utf-8"
|
9
|
+
|
10
|
+
# Extract the requested path from the request
|
11
|
+
path = Rack::Utils.unescape(env['PATH_INFO'])
|
12
|
+
index_file = root + "#{path}/index.html"
|
13
|
+
|
14
|
+
if File.exists?(index_file)
|
15
|
+
# Return the index
|
16
|
+
[200, {'Content-Type' => 'text/html' + default_charset}, [File.read(index_file)]]
|
17
|
+
else
|
18
|
+
# Pass the request to the directory app
|
19
|
+
response = Rack::Directory.new(root).call(env)
|
20
|
+
if response[1]['Content-Type']
|
21
|
+
response[1]['Content-Type'] += default_charset
|
22
|
+
end
|
23
|
+
response
|
24
|
+
end
|
25
|
+
}
|
26
|
+
|
27
|
+
Rack::Server.new(:app => rack_app,
|
28
|
+
:Port => 3000,
|
29
|
+
:server => 'webrick'
|
30
|
+
).start
|
31
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Writecast
|
2
|
+
# Site configuration
|
3
|
+
SITE_TITLE ||= "my site title (change this)"
|
4
|
+
SEPARATOR ||= " / "
|
5
|
+
INDEX_TITLE ||= "blog"
|
6
|
+
NUM_RECENT_POSTS ||= 5
|
7
|
+
DOC_EXTS ||= ['.post', '.page', '.txt'] # must be lower case
|
8
|
+
PRETTY_HTML = true unless defined? PRETTY_HTML
|
9
|
+
KRAMDOWN_OPTS = {:coderay_line_numbers => :table, :coderay_css => :class}
|
10
|
+
# Atom feed configuration
|
11
|
+
SITE_URI ||= "http://mydomain.net"
|
12
|
+
SITE_AUTHOR ||= "Anonymous Coward"
|
13
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'uuidtools'
|
3
|
+
require 'atom'
|
4
|
+
require_relative 'views/base'
|
5
|
+
|
6
|
+
module Writecast
|
7
|
+
|
8
|
+
# represents a document; either a post or a page.
|
9
|
+
class Document
|
10
|
+
|
11
|
+
attr_reader :title, :time, :summary, :body, :tags, :link_path, :file_path, :raw_file_path
|
12
|
+
|
13
|
+
def initialize(file_path)
|
14
|
+
doc_string = IO.read(file_path)
|
15
|
+
@raw_file_path = file_path
|
16
|
+
|
17
|
+
if doc_string =~ /(\A---\n(.*)---\n)?(.*)/m
|
18
|
+
doc_header = ($2 ? YAML.load($2) : {}) || {}
|
19
|
+
summary,body = $3.split("\n\n",2)
|
20
|
+
else
|
21
|
+
raise ArgumentError, "Parse error in #{file_path}."
|
22
|
+
end
|
23
|
+
|
24
|
+
file_basename = File.basename(file_path)
|
25
|
+
|
26
|
+
@title = doc_header['title'] || file_basename.chomp(File.extname(file_path))
|
27
|
+
@time = doc_header['time'] || File.stat(file_path).mtime
|
28
|
+
@tags = doc_header['tags']
|
29
|
+
|
30
|
+
if summary
|
31
|
+
# the first regex matches kramdown image syntax, and stores two backreferences
|
32
|
+
# the second just matches the syntax
|
33
|
+
image_regex_select = /(!\[.*\]\()(.*)\)/
|
34
|
+
image_regex = /!\[.*\]\(.*\)/
|
35
|
+
|
36
|
+
# swap all relative image paths with absolute paths
|
37
|
+
image_swaps = {}
|
38
|
+
summary.scan(image_regex_select).zip(summary.scan(image_regex)) do |ab,image_match|
|
39
|
+
a,b = ab
|
40
|
+
image_swaps[image_match] = "#{a}/#{"%04d" % @time.year}/" +
|
41
|
+
"#{"%02d" % @time.month}/#{file_basename}_static/" +
|
42
|
+
"#{File.basename(b)})"
|
43
|
+
end
|
44
|
+
@summary = summary.gsub(image_regex, image_swaps).chomp
|
45
|
+
else
|
46
|
+
@summary = ''
|
47
|
+
end
|
48
|
+
|
49
|
+
@body = body ? body.chomp : ''
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Post < Document
|
54
|
+
|
55
|
+
include UUIDTools
|
56
|
+
|
57
|
+
attr_reader :id
|
58
|
+
|
59
|
+
def initialize(file_path)
|
60
|
+
super
|
61
|
+
path_dir = ["/%04d" % @time.year, "/%02d" % @time.month, "/%02d-" % @time.day].join
|
62
|
+
@link_path = path_dir + "%s.html" % CGI.escape(@title)
|
63
|
+
@file_path = path_dir + "%s.html" % @title
|
64
|
+
@id = "urn:uuid:#{UUID.sha1_create(UUID_URL_NAMESPACE, SITE_URI + @link_path).to_s}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_html(pages)
|
68
|
+
title_tag = SITE_TITLE +
|
69
|
+
@time.strftime("#{SEPARATOR}%Y#{SEPARATOR}%B#{SEPARATOR}%d#{SEPARATOR}") + @title
|
70
|
+
Post_view.new(:doc => self, :pages => pages, :title_tag => title_tag
|
71
|
+
).to_html(:prettyprint => PRETTY_HTML)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Page < Document
|
76
|
+
|
77
|
+
def initialize(file_path)
|
78
|
+
super
|
79
|
+
@link_path = "/%s.html" % CGI.escape(@title)
|
80
|
+
@file_path = "/%s.html" % @title
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_html(pages)
|
84
|
+
title_tag = SITE_TITLE + SEPARATOR + @title
|
85
|
+
Page_view.new(:doc => self, :pages => pages, :title_tag => title_tag
|
86
|
+
).to_html(:prettyprint => PRETTY_HTML)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class Index < Document
|
91
|
+
|
92
|
+
include UUIDTools
|
93
|
+
|
94
|
+
def initialize(posts_hash, tags_hash, num_posts)
|
95
|
+
@title = INDEX_TITLE # used to indicate 'selected' in the menu
|
96
|
+
@file_path = 'index.html'
|
97
|
+
@link_path = '/'
|
98
|
+
@posts_hash = posts_hash
|
99
|
+
@tags_hash = tags_hash
|
100
|
+
@num_posts = num_posts
|
101
|
+
@recent_posts = recent_posts
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_html(pages)
|
105
|
+
years = @posts_hash.keys
|
106
|
+
Index_view.new(:doc => self, :pages => pages, :recent_posts => @recent_posts,
|
107
|
+
:years => years, :title_tag => SITE_TITLE, :tags_hash => @tags_hash,
|
108
|
+
:num_posts => @num_posts).to_html(:prettyprint => PRETTY_HTML)
|
109
|
+
end
|
110
|
+
|
111
|
+
def to_atom
|
112
|
+
feed = Atom::Feed.new do |f|
|
113
|
+
f.title = SITE_TITLE
|
114
|
+
f.links << Atom::Link.new(:href => SITE_URI)
|
115
|
+
f.updated = Time.now
|
116
|
+
f.authors << Atom::Person.new(:name => SITE_AUTHOR)
|
117
|
+
f.id = "urn:uuid:#{UUID.sha1_create(UUID_URL_NAMESPACE, SITE_URI).to_s}"
|
118
|
+
@recent_posts.each do |post|
|
119
|
+
f.entries << Atom::Entry.new do |e|
|
120
|
+
e.title = post.title
|
121
|
+
e.links << Atom::Link.new(:href => SITE_URI + post.link_path)
|
122
|
+
e.id = post.id
|
123
|
+
e.updated = post.time
|
124
|
+
e.summary = post.summary
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
feed.to_xml
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def recent_posts
|
134
|
+
recent = []
|
135
|
+
@posts_hash.each do |year,year_posts|
|
136
|
+
year_posts.sort{|a,b| b.first <=> a.first}.each do |month,month_posts|
|
137
|
+
month_posts.sort!{|a,b| b.time <=> a.time}.each do |post|
|
138
|
+
recent << post
|
139
|
+
return recent if recent.length >= NUM_RECENT_POSTS
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
return recent # if < NUM_RECENT_POSTS
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class Year_index
|
148
|
+
|
149
|
+
def initialize(year, month_hash)
|
150
|
+
@year = year
|
151
|
+
@month_hash = month_hash
|
152
|
+
end
|
153
|
+
|
154
|
+
def to_html(pages)
|
155
|
+
title_tag = SITE_TITLE + SEPARATOR + ("#{@year} Archives")
|
156
|
+
# :doc is only passed here for the menu selector.
|
157
|
+
# There's definitely a better way to do this.
|
158
|
+
Year_index_view.new(:doc => self, :year => @year, :months_hash => @month_hash,
|
159
|
+
:pages => pages, :title_tag => title_tag
|
160
|
+
).to_html(:prettyprint => PRETTY_HTML)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class Month_index
|
165
|
+
|
166
|
+
def initialize(year, month, month_posts)
|
167
|
+
@year = year
|
168
|
+
@month = month
|
169
|
+
@month_posts = month_posts
|
170
|
+
end
|
171
|
+
|
172
|
+
def to_html(pages)
|
173
|
+
title_tag = SITE_TITLE + SEPARATOR + @year.to_s + SEPARATOR + Date::MONTHNAMES[@month]
|
174
|
+
Month_index_view.new(:doc => self, :year => @year, :month => @month,
|
175
|
+
:posts => @month_posts, :pages => pages,
|
176
|
+
:title_tag => title_tag
|
177
|
+
).to_html(:prettyprint => PRETTY_HTML)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class Tag_index
|
182
|
+
|
183
|
+
include UUIDTools
|
184
|
+
|
185
|
+
def initialize(tag_set, posts)
|
186
|
+
@tag,@escaped_tag = tag_set
|
187
|
+
@posts = posts
|
188
|
+
end
|
189
|
+
|
190
|
+
def to_html(pages)
|
191
|
+
title_tag = SITE_TITLE + SEPARATOR + "tag: #{@tag}"
|
192
|
+
Tag_index_view.new(:doc => self, :tag => @tag, :escaped_tag => @escaped_tag,
|
193
|
+
:posts => @posts, :pages => pages, :title_tag => title_tag
|
194
|
+
).to_html(:prettyprint => PRETTY_HTML)
|
195
|
+
end
|
196
|
+
|
197
|
+
def to_atom
|
198
|
+
feed = Atom::Feed.new do |f|
|
199
|
+
f.title = SITE_TITLE
|
200
|
+
f.links << Atom::Link.new(:href => SITE_URI)
|
201
|
+
f.updated = Time.now
|
202
|
+
f.authors << Atom::Person.new(:name => SITE_AUTHOR)
|
203
|
+
f.id = "urn:uuid:" +
|
204
|
+
UUID.sha1_create(UUID_URL_NAMESPACE,
|
205
|
+
SITE_URI + "tags/#{@escaped_tag}").to_s
|
206
|
+
@posts.each do |post|
|
207
|
+
f.entries << Atom::Entry.new do |e|
|
208
|
+
e.title = post.title
|
209
|
+
e.links << Atom::Link.new(:href => SITE_URI + post.link_path)
|
210
|
+
e.id = post.id
|
211
|
+
e.updated = post.time
|
212
|
+
e.summary = post.summary
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
feed.to_xml
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
class All_tags_index
|
221
|
+
|
222
|
+
def initialize(tags_hash)
|
223
|
+
@tags_hash = tags_hash
|
224
|
+
end
|
225
|
+
|
226
|
+
def to_html(pages)
|
227
|
+
title_tag = SITE_TITLE + SEPARATOR + "All tags"
|
228
|
+
All_tags_index_view.new(:doc => self, :tags_hash => @tags_hash,
|
229
|
+
:pages => pages, :title_tag => title_tag
|
230
|
+
).to_html(:prettyprint => PRETTY_HTML)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Writecast
|
2
|
+
class All_tags_index_view < Base_view
|
3
|
+
|
4
|
+
def render_breadcrumb
|
5
|
+
p SEPARATOR + "all tags"
|
6
|
+
end
|
7
|
+
|
8
|
+
def render_content_inner
|
9
|
+
div :id => 'tags_list' do
|
10
|
+
ul do
|
11
|
+
@tags_hash.each do |tag_set,posts|
|
12
|
+
tag,escaped_tag = tag_set
|
13
|
+
li do
|
14
|
+
p do
|
15
|
+
a tag, :href => "/tags/#{escaped_tag}"
|
16
|
+
text " - #{posts.size} posts"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'erector'
|
2
|
+
require 'kramdown'
|
3
|
+
|
4
|
+
module Writecast
|
5
|
+
class Base_view < Erector::Widget
|
6
|
+
|
7
|
+
def content
|
8
|
+
rawtext '<!DOCTYPE HTML>'
|
9
|
+
html do
|
10
|
+
head {render_head}
|
11
|
+
body do
|
12
|
+
div(:id => 'container') do
|
13
|
+
render_body_top_inner
|
14
|
+
div(:id => 'content') {render_content_inner}
|
15
|
+
render_footer
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def render_head
|
22
|
+
render_feed_link if defined? render_feed_link
|
23
|
+
css "/static/style.css"
|
24
|
+
css "/static/coderay.css"
|
25
|
+
script :src => "/static/jquery.min.js", :type => "text/javascript"
|
26
|
+
script :src => "/static/jquery.form.js", :type => "text/javascript"
|
27
|
+
script :src => "/static/jquery.validate.js", :type => "text/javascript"
|
28
|
+
script :src => "/static/comment.js", :type => "text/javascript"
|
29
|
+
title @title_tag
|
30
|
+
end
|
31
|
+
|
32
|
+
def render_body_top_inner
|
33
|
+
div(:id => 'header') {p SITE_TITLE}
|
34
|
+
div(:id => 'menu') {render_menu}
|
35
|
+
div(:id => 'breadcrumb') {render_breadcrumb}
|
36
|
+
end
|
37
|
+
|
38
|
+
def render_footer
|
39
|
+
div :id => 'footer' do
|
40
|
+
p a 'valid html5', :href => 'http://validator.w3.org/check/referer'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def render_menu
|
45
|
+
ul :id => 'tabnav' do
|
46
|
+
@pages.each do |page|
|
47
|
+
block = ->{a page.title, :href => page.link_path}
|
48
|
+
if ((@doc.is_a?(Page) || @doc.is_a?(Index)) && page.title == @doc.title) ||
|
49
|
+
((@doc.is_a?(Post) || @doc.is_a?(Month_index) ||
|
50
|
+
@doc.is_a?(Year_index) || @doc.is_a?(Tag_index) ||
|
51
|
+
@doc.is_a?(All_tags_index)) && page.title == INDEX_TITLE)
|
52
|
+
li :id => 'selected', &block
|
53
|
+
else
|
54
|
+
li &block
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# render a summary of a single post (as a li)
|
61
|
+
def render_post_summary(post)
|
62
|
+
li do
|
63
|
+
p do
|
64
|
+
a post.title, :href => post.link_path
|
65
|
+
br
|
66
|
+
text "last modified: #{post.time}"
|
67
|
+
end
|
68
|
+
rawtext Kramdown::Document.new(post.summary, KRAMDOWN_OPTS).to_html
|
69
|
+
p {a "more...", :href => post.link_path}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# specific views subclassed from Base_view
|
76
|
+
|
77
|
+
# try to load from the override dir first
|
78
|
+
def Writecast::loadviews
|
79
|
+
def loadview view
|
80
|
+
view_dir = File.join(Dir.pwd, 'views')
|
81
|
+
begin
|
82
|
+
require File.join(view_dir, view)
|
83
|
+
rescue LoadError
|
84
|
+
require_relative view
|
85
|
+
end
|
86
|
+
end
|
87
|
+
views = 'index','post','page','month_index','year_index','tag_index','all_tags_index'
|
88
|
+
views.each {|view| loadview view}
|
89
|
+
end
|
90
|
+
Writecast::loadviews
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Writecast
|
2
|
+
class Index_view < Base_view
|
3
|
+
|
4
|
+
def render_feed_link
|
5
|
+
link :rel => 'alternate', :type => 'application/rss+xml', :href=> '/feed.atom'
|
6
|
+
end
|
7
|
+
|
8
|
+
def render_breadcrumb
|
9
|
+
p SEPARATOR
|
10
|
+
end
|
11
|
+
|
12
|
+
def render_content_inner
|
13
|
+
div :id => 'sidebar' do
|
14
|
+
div :id => 'tags' do
|
15
|
+
p do
|
16
|
+
text "tag cloud ("
|
17
|
+
a 'view list', :href => '/tags'
|
18
|
+
text ")"
|
19
|
+
end
|
20
|
+
p do
|
21
|
+
@tags_hash.each do |tag_set,posts|
|
22
|
+
tag,escaped_tag = tag_set
|
23
|
+
size = 200*(posts.size.to_f/@num_posts)
|
24
|
+
span :style => "font-size: #{50 + size}%;" do
|
25
|
+
a tag, :href => "/tags/#{escaped_tag}"
|
26
|
+
text ' '
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
div :id => 'archive' do
|
32
|
+
p 'post archive'
|
33
|
+
ul do
|
34
|
+
@years.each do |year|
|
35
|
+
li do
|
36
|
+
a year, :href => "/#{year}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
div :id => 'recent_posts' do
|
43
|
+
ul do
|
44
|
+
@recent_posts.each do |post|
|
45
|
+
render_post_summary(post) # defined in base
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Writecast
|
2
|
+
class Month_index_view < Base_view
|
3
|
+
|
4
|
+
def render_breadcrumb
|
5
|
+
p do
|
6
|
+
text SEPARATOR
|
7
|
+
a @year, :href => "/#{@year}"
|
8
|
+
text SEPARATOR + "%02d" % @month
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def render_content_inner
|
13
|
+
div :id => 'post_summaries' do
|
14
|
+
ul do
|
15
|
+
@posts.each do |post|
|
16
|
+
render_post_summary(post)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Writecast
|
2
|
+
class Page_view < Base_view
|
3
|
+
|
4
|
+
def render_breadcrumb
|
5
|
+
p text SEPARATOR + @doc.title
|
6
|
+
end
|
7
|
+
|
8
|
+
def render_content_inner
|
9
|
+
div(:id => 'meta') {p {text "last modified: #{@doc.time}"}}
|
10
|
+
div :id => 'summary' do
|
11
|
+
rawtext Kramdown::Document.new(@doc.summary, KRAMDOWN_OPTS).to_html
|
12
|
+
end
|
13
|
+
div :id => 'body' do
|
14
|
+
rawtext Kramdown::Document.new(@doc.body, KRAMDOWN_OPTS).to_html
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Writecast
|
2
|
+
class Post_view < Base_view
|
3
|
+
|
4
|
+
def render_breadcrumb
|
5
|
+
day,month,year = @doc.time.day,@doc.time.month,@doc.time.year
|
6
|
+
p do
|
7
|
+
text SEPARATOR
|
8
|
+
a year, :href => "/#{year}"
|
9
|
+
text SEPARATOR
|
10
|
+
a "%02d" % month, :href => "/#{year}/#{"%02d" % month}"
|
11
|
+
text SEPARATOR + "#{day}" + SEPARATOR + @doc.title
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def render_content_inner
|
16
|
+
div(:id => 'meta') {p {text "last modified: #{@doc.time}"}}
|
17
|
+
div :id => 'summary' do
|
18
|
+
rawtext Kramdown::Document.new(@doc.summary, KRAMDOWN_OPTS).to_html
|
19
|
+
end
|
20
|
+
div :id => 'body' do
|
21
|
+
rawtext Kramdown::Document.new(@doc.body, KRAMDOWN_OPTS).to_html
|
22
|
+
end
|
23
|
+
div :id => 'tags' do
|
24
|
+
p do
|
25
|
+
text 'tagged: '
|
26
|
+
@doc.tags and @doc.tags.each do |tag|
|
27
|
+
a tag, :href => "/tags/#{CGI.escape(tag)}"
|
28
|
+
text ', ' if tag != @doc.tags.last
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
div :id => 'comments' do
|
33
|
+
p 'comments:'
|
34
|
+
p a 'show comment form', {:id => 'toggle_comment_form', :href => '#'}
|
35
|
+
div :id => 'comment_form_wrapper', :style => 'display:none;' do
|
36
|
+
form :id => 'comment_form', :action => "/comment", :method => "post" do
|
37
|
+
label :id => 'name' do
|
38
|
+
text 'name'
|
39
|
+
input :type => 'text', :name => 'name', :class => 'required'
|
40
|
+
end
|
41
|
+
label :id => 'first_name' do
|
42
|
+
text 'first name'
|
43
|
+
input :type => 'text', :name => 'first_name'
|
44
|
+
end
|
45
|
+
label :id => 'email' do
|
46
|
+
text 'email'
|
47
|
+
input :type => 'text', :name => 'email', :class => 'required'
|
48
|
+
end
|
49
|
+
input :type => 'hidden', :name => 'file_path', :value => @doc.raw_file_path
|
50
|
+
br
|
51
|
+
label :id => 'comment' do
|
52
|
+
text 'comment'
|
53
|
+
textarea :name => 'comment', :class => 'required'
|
54
|
+
end
|
55
|
+
br
|
56
|
+
input :id => 'reset', :type => "reset", :value => "clear form"
|
57
|
+
input :id => 'submit', :type => "submit", :value => "send comment"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Writecast
|
2
|
+
class Tag_index_view < Base_view
|
3
|
+
|
4
|
+
def render_feed_link
|
5
|
+
link ({
|
6
|
+
:rel => 'alternate',
|
7
|
+
:type => 'application/rss+xml',
|
8
|
+
:href=> "/tags/#{@escaped_tag}/feed.atom"
|
9
|
+
})
|
10
|
+
end
|
11
|
+
|
12
|
+
def render_breadcrumb
|
13
|
+
p do
|
14
|
+
text SEPARATOR
|
15
|
+
a "tags", :href => '/tags'
|
16
|
+
text ": #{@tag}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def render_content_inner
|
21
|
+
div :id => 'post_summaries' do
|
22
|
+
ul {@posts.each {|post| render_post_summary(post)}}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Writecast
|
2
|
+
class Year_index_view < Base_view
|
3
|
+
|
4
|
+
def render_breadcrumb
|
5
|
+
p SEPARATOR + "%04d" % @year
|
6
|
+
end
|
7
|
+
|
8
|
+
def render_content_inner
|
9
|
+
div :id => 'months_list' do
|
10
|
+
ul do
|
11
|
+
@months_hash.each do |month,posts|
|
12
|
+
li do
|
13
|
+
a "%02d" % month, :href => "/#{"%04d" % @year}/#{"%02d" % month}"
|
14
|
+
text " (#{Date::MONTHNAMES[month]}) - #{posts.size} posts"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/writecast.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "writecast/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'writecast'
|
7
|
+
s.version = Writecast::VERSION
|
8
|
+
s.authors = ['Scott Leggett']
|
9
|
+
s.email = ['sml@internode.on.net']
|
10
|
+
s.homepage = 'https://github.com/smlx/writecast'
|
11
|
+
s.summary = %q{A small static site generator.}
|
12
|
+
s.description = %q{A small static site generator which will take a bunch of your writings and turn them into HTML.}
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency 'kramdown' # markdown's successor
|
20
|
+
s.add_dependency 'erector' # generate html from ruby code
|
21
|
+
s.add_dependency 'activesupport' # suppresses annoying error message, and enables Erector::Widgets::Table
|
22
|
+
s.add_dependency 'i18n' # soft dependency of activesupport
|
23
|
+
s.add_dependency 'coderay' # syntax highlighting
|
24
|
+
s.add_dependency 'rack' # enable server
|
25
|
+
s.add_dependency 'ratom' # generate atom feeds
|
26
|
+
s.add_dependency 'uuidtools' # generate atom entry id's
|
27
|
+
|
28
|
+
s.add_development_dependency 'rspec'
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: writecast
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Scott Leggett
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-10-04 00:00:00 +08:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: kramdown
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: erector
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :runtime
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: activesupport
|
48
|
+
prerelease: false
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
segments:
|
55
|
+
- 0
|
56
|
+
version: "0"
|
57
|
+
type: :runtime
|
58
|
+
version_requirements: *id003
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: i18n
|
61
|
+
prerelease: false
|
62
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
type: :runtime
|
71
|
+
version_requirements: *id004
|
72
|
+
- !ruby/object:Gem::Dependency
|
73
|
+
name: coderay
|
74
|
+
prerelease: false
|
75
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
type: :runtime
|
84
|
+
version_requirements: *id005
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: rack
|
87
|
+
prerelease: false
|
88
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
type: :runtime
|
97
|
+
version_requirements: *id006
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: ratom
|
100
|
+
prerelease: false
|
101
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
type: :runtime
|
110
|
+
version_requirements: *id007
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: uuidtools
|
113
|
+
prerelease: false
|
114
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
segments:
|
120
|
+
- 0
|
121
|
+
version: "0"
|
122
|
+
type: :runtime
|
123
|
+
version_requirements: *id008
|
124
|
+
- !ruby/object:Gem::Dependency
|
125
|
+
name: rspec
|
126
|
+
prerelease: false
|
127
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
segments:
|
133
|
+
- 0
|
134
|
+
version: "0"
|
135
|
+
type: :development
|
136
|
+
version_requirements: *id009
|
137
|
+
description: A small static site generator which will take a bunch of your writings and turn them into HTML.
|
138
|
+
email:
|
139
|
+
- sml@internode.on.net
|
140
|
+
executables:
|
141
|
+
- writecast
|
142
|
+
extensions: []
|
143
|
+
|
144
|
+
extra_rdoc_files: []
|
145
|
+
|
146
|
+
files:
|
147
|
+
- .gitignore
|
148
|
+
- Gemfile
|
149
|
+
- Rakefile
|
150
|
+
- TODO
|
151
|
+
- bin/writecast
|
152
|
+
- lib/writecast.rb
|
153
|
+
- lib/writecast/commands/help.rb
|
154
|
+
- lib/writecast/commands/init.rb
|
155
|
+
- lib/writecast/commands/override.rb
|
156
|
+
- lib/writecast/commands/process.rb
|
157
|
+
- lib/writecast/commands/serve.rb
|
158
|
+
- lib/writecast/config.rb
|
159
|
+
- lib/writecast/models.rb
|
160
|
+
- lib/writecast/version.rb
|
161
|
+
- lib/writecast/views/all_tags_index.rb
|
162
|
+
- lib/writecast/views/base.rb
|
163
|
+
- lib/writecast/views/index.rb
|
164
|
+
- lib/writecast/views/month_index.rb
|
165
|
+
- lib/writecast/views/page.rb
|
166
|
+
- lib/writecast/views/post.rb
|
167
|
+
- lib/writecast/views/tag_index.rb
|
168
|
+
- lib/writecast/views/year_index.rb
|
169
|
+
- writecast.gemspec
|
170
|
+
has_rdoc: true
|
171
|
+
homepage: https://github.com/smlx/writecast
|
172
|
+
licenses: []
|
173
|
+
|
174
|
+
post_install_message:
|
175
|
+
rdoc_options: []
|
176
|
+
|
177
|
+
require_paths:
|
178
|
+
- lib
|
179
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
180
|
+
none: false
|
181
|
+
requirements:
|
182
|
+
- - ">="
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
segments:
|
185
|
+
- 0
|
186
|
+
version: "0"
|
187
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
188
|
+
none: false
|
189
|
+
requirements:
|
190
|
+
- - ">="
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
segments:
|
193
|
+
- 0
|
194
|
+
version: "0"
|
195
|
+
requirements: []
|
196
|
+
|
197
|
+
rubyforge_project:
|
198
|
+
rubygems_version: 1.3.7
|
199
|
+
signing_key:
|
200
|
+
specification_version: 3
|
201
|
+
summary: A small static site generator.
|
202
|
+
test_files: []
|
203
|
+
|