zzot-semi-static 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +6 -0
- data/Manifest.txt +30 -0
- data/README.markdown +84 -0
- data/VERSION.yml +4 -0
- data/bin/semi +6 -0
- data/lib/semi-static.rb +30 -0
- data/lib/semi-static/base.rb +74 -0
- data/lib/semi-static/categories.rb +74 -0
- data/lib/semi-static/cli.rb +126 -0
- data/lib/semi-static/convertable.rb +94 -0
- data/lib/semi-static/core_ext/hash.rb +19 -0
- data/lib/semi-static/index.rb +12 -0
- data/lib/semi-static/layout.rb +14 -0
- data/lib/semi-static/page.rb +58 -0
- data/lib/semi-static/post.rb +133 -0
- data/lib/semi-static/posts.rb +110 -0
- data/lib/semi-static/pygmentize.rb +54 -0
- data/lib/semi-static/site.rb +232 -0
- data/lib/semi-static/snippet.rb +12 -0
- data/lib/semi-static/statistics.rb +45 -0
- data/lib/semi-static/stylesheet.rb +33 -0
- data/test/helper.rb +111 -0
- data/test/ref/test_layout/default_layout.html +35 -0
- data/test/ref/test_layout/post_layout.html +85 -0
- data/test/ref/test_output/2005-03-27.html +5 -0
- data/test/ref/test_output/2005-03.html +4 -0
- data/test/ref/test_output/2005.html +5 -0
- data/test/ref/test_output/2008-11-24.html +5 -0
- data/test/ref/test_output/2008-11-26.html +5 -0
- data/test/ref/test_output/2008-11.html +5 -0
- data/test/ref/test_output/2008-12-04.html +5 -0
- data/test/ref/test_output/2008-12.html +4 -0
- data/test/ref/test_output/2008.html +6 -0
- data/test/ref/test_page/about.html +47 -0
- data/test/ref/test_page/colophon.html +45 -0
- data/test/ref/test_post/impressions.html +102 -0
- data/test/ref/test_post/lighting-up.html +102 -0
- data/test/ref/test_post/the-working-mans-typeface.html +88 -0
- data/test/source/indices/day.erb +7 -0
- data/test/source/indices/month.erb +10 -0
- data/test/source/indices/year.erb +10 -0
- data/test/source/layouts/default.haml +25 -0
- data/test/source/layouts/post.erb +36 -0
- data/test/source/pages/about.md +38 -0
- data/test/source/pages/colophon.md +36 -0
- data/test/source/pages/feed.xml.erb +26 -0
- data/test/source/posts/2005-03-27-a-bash-script-to-mess-with-the-containing-terminalapp-window.markdown +38 -0
- data/test/source/posts/2008-11-24-lighting-up.markdown +41 -0
- data/test/source/posts/2008-11-26-impressions.md +42 -0
- data/test/source/posts/2008-12-04-the-working-mans-typeface.html +15 -0
- data/test/source/scripts/jquery-1.3.js +4241 -0
- data/test/source/scripts/jquery-1.3.min.js +19 -0
- data/test/source/semi.yml +5 -0
- data/test/source/snippets/comment-links.html +17 -0
- data/test/source/snippets/comments.html +4 -0
- data/test/source/stylesheets/layout.sass +115 -0
- data/test/source/stylesheets/post.sass +21 -0
- data/test/source/stylesheets/screen.sass +11 -0
- data/test/source/stylesheets/syntax.css +60 -0
- data/test/source/stylesheets/text.sass +25 -0
- data/test/test_layout.rb +51 -0
- data/test/test_output.rb +61 -0
- data/test/test_page.rb +68 -0
- data/test/test_post.rb +96 -0
- metadata +183 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
module SemiStatic
|
2
|
+
module Convertable
|
3
|
+
include ERB::Util
|
4
|
+
|
5
|
+
def load
|
6
|
+
@content = nil
|
7
|
+
if layout
|
8
|
+
layout.load
|
9
|
+
end
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def content(options={})
|
14
|
+
return @content unless @content.nil?
|
15
|
+
|
16
|
+
options = { :page => self }.merge(options)
|
17
|
+
for name, value in options
|
18
|
+
eval "#{name.to_s} = options[name]"
|
19
|
+
end
|
20
|
+
|
21
|
+
site.stats.record(self.class.name.split('::').last, source_path) do
|
22
|
+
case self.source_ext
|
23
|
+
when '.md', '.markdown'
|
24
|
+
@content = Maruku.new(self.source_content).to_html
|
25
|
+
when '.haml'
|
26
|
+
Haml::Engine.new(self.source_content, :filename => source_path).render(binding)
|
27
|
+
when '.erb'
|
28
|
+
ERB.new(self.source_content, nil, '-').result(binding)
|
29
|
+
when '.html'
|
30
|
+
self.source_content
|
31
|
+
else
|
32
|
+
raise ArgumentError, "Unsupported format: #{self.source_path}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def layout_name
|
38
|
+
unless source_metadata.nil? || !source_metadata.include?(:layout)
|
39
|
+
source_metadata[:layout].to_sym
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def layout
|
44
|
+
if layout_name.nil?
|
45
|
+
return nil
|
46
|
+
else
|
47
|
+
return site.layouts[layout_name.to_sym]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def source_mtime
|
52
|
+
mtime = super
|
53
|
+
if layout && layout.source_mtime > mtime
|
54
|
+
mtime = layout.source_mtime
|
55
|
+
end
|
56
|
+
return mtime
|
57
|
+
end
|
58
|
+
|
59
|
+
def render(options={})
|
60
|
+
content = self.content(options)
|
61
|
+
if layout
|
62
|
+
options = { :page => self }.merge options
|
63
|
+
content = layout.render(options.merge( :content => content ))
|
64
|
+
end
|
65
|
+
return content
|
66
|
+
end
|
67
|
+
|
68
|
+
def snippet(name)
|
69
|
+
name = name.to_s
|
70
|
+
site.snippets[name].render :page => self
|
71
|
+
end
|
72
|
+
|
73
|
+
# This method is adapted from Haml::Buffer#parse_object_ref -- it's
|
74
|
+
# used to make it easier for Haml and ERB layouts to generate the
|
75
|
+
# same output so I can use the same test output for both.
|
76
|
+
def object_ref(object)
|
77
|
+
return '' if object.nil?
|
78
|
+
name = underscore(object.class)
|
79
|
+
id = "#{name}_#{object.id || 'new'}"
|
80
|
+
return "class='#{name}' id='#{id}'"
|
81
|
+
end
|
82
|
+
|
83
|
+
# Changes a word from camel case to underscores. Based on the method
|
84
|
+
# of the same name in Rails' Inflector, but copied here so it'll run
|
85
|
+
# properly without Rails.
|
86
|
+
def underscore(camel_cased_word)
|
87
|
+
camel_cased_word.to_s.gsub(/::/, '_').
|
88
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
89
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
90
|
+
tr("-", "_").
|
91
|
+
downcase
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module SemiStatic
|
2
|
+
module CoreExt #:nodoc:
|
3
|
+
module Hash
|
4
|
+
# Replace self with a hash whose keys have been converted to symbols.
|
5
|
+
def symbolize_keys
|
6
|
+
hash = to_a.inject({}) do |memo,item|
|
7
|
+
key, value = item
|
8
|
+
memo[key.to_sym] = value
|
9
|
+
memo
|
10
|
+
end
|
11
|
+
replace(hash)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Hash #:nodoc:
|
18
|
+
include SemiStatic::CoreExt::Hash
|
19
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module SemiStatic
|
2
|
+
# Page represents a static page in the site. Page itself only
|
3
|
+
# represents a single source file, but can have a Layout and any number
|
4
|
+
# of Snippets, each of which has its own source file. Page's modification
|
5
|
+
# time is the most recent of itself, its layout, and any Snippets used.
|
6
|
+
class Page < Base
|
7
|
+
include Convertable
|
8
|
+
|
9
|
+
##
|
10
|
+
# The Page's output directory
|
11
|
+
attr_reader :output_dir
|
12
|
+
|
13
|
+
##
|
14
|
+
# The Page's output path
|
15
|
+
attr_reader :output_path
|
16
|
+
|
17
|
+
##
|
18
|
+
# The Page's name
|
19
|
+
attr_reader :name
|
20
|
+
|
21
|
+
##
|
22
|
+
# The Page's URI
|
23
|
+
attr_reader :uri
|
24
|
+
|
25
|
+
##
|
26
|
+
# Initializes a new Page
|
27
|
+
#
|
28
|
+
# +site+:: The Site object we belong to
|
29
|
+
# +path+:: The relative path to the source file
|
30
|
+
def initialize(site, path)
|
31
|
+
super
|
32
|
+
@metadata = [ :title, :layout ]
|
33
|
+
|
34
|
+
src_base = File.basename(source_path, source_ext)
|
35
|
+
output_ext = File.extname(src_base)
|
36
|
+
if output_ext.nil? || output_ext.empty?
|
37
|
+
output_ext = '.html'
|
38
|
+
else
|
39
|
+
src_base = File.basename(src_base, output_ext)
|
40
|
+
end
|
41
|
+
@output_dir, src_file = File.split(source_path)
|
42
|
+
|
43
|
+
if output_dir == '.'
|
44
|
+
prefix = ''
|
45
|
+
else
|
46
|
+
prefix = "#{output_dir}/"
|
47
|
+
end
|
48
|
+
@name = "/#{prefix}#{src_base}"
|
49
|
+
@output_path = "#{prefix}#{src_base}#{output_ext}"
|
50
|
+
|
51
|
+
if src_base == 'index'
|
52
|
+
@uri = "/#{output_dir}/"
|
53
|
+
else
|
54
|
+
@uri = "/#{output_path}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module SemiStatic
|
2
|
+
# Post represents a single post in the site. Post itself only
|
3
|
+
# represents a single source file, but can have a Layout and any number
|
4
|
+
# of Snippets, each of which has its own source file. Post's modification
|
5
|
+
# time is the most recent of itself, its Layout, and any Snippets used.
|
6
|
+
class Post < Base
|
7
|
+
include Convertable
|
8
|
+
|
9
|
+
##
|
10
|
+
# The Post's name
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
##
|
14
|
+
# The Post's Category
|
15
|
+
attr_reader :category
|
16
|
+
|
17
|
+
##
|
18
|
+
# The Post's Tags
|
19
|
+
attr_reader :tags
|
20
|
+
|
21
|
+
##
|
22
|
+
# The Post's output directory
|
23
|
+
attr_reader :output_dir
|
24
|
+
|
25
|
+
##
|
26
|
+
# The Post's output path
|
27
|
+
attr_reader :output_path
|
28
|
+
|
29
|
+
##
|
30
|
+
# The Post's slug
|
31
|
+
attr_reader :slug
|
32
|
+
|
33
|
+
##
|
34
|
+
# The Post's URI
|
35
|
+
attr_reader :uri
|
36
|
+
|
37
|
+
##
|
38
|
+
# Date Post was created
|
39
|
+
attr_reader :created
|
40
|
+
|
41
|
+
##
|
42
|
+
# Date the Post was last updated
|
43
|
+
attr_reader :updated
|
44
|
+
|
45
|
+
# attr_reader :year
|
46
|
+
# attr_reader :month
|
47
|
+
# attr_reader :day
|
48
|
+
|
49
|
+
##
|
50
|
+
# Initializes a new Post
|
51
|
+
#
|
52
|
+
# +site+:: The Site object we belong to
|
53
|
+
# +path+:: The relative path to the source file
|
54
|
+
def initialize(site, path)
|
55
|
+
super
|
56
|
+
@metadata = [ :title, :layout, :author ]
|
57
|
+
|
58
|
+
@name = File.basename(source_path, source_ext)
|
59
|
+
|
60
|
+
@category = site.categories[source_metadata[:category]]
|
61
|
+
@category << self
|
62
|
+
|
63
|
+
@tags = []
|
64
|
+
unless source_metadata[:tags].nil?
|
65
|
+
for tag in source_metadata[:tags]
|
66
|
+
@tags << site.tags[tag]
|
67
|
+
@tags.last << self
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
if name =~ /^(\d+-\d+-\d+)-(.+)$/
|
72
|
+
@created = Time.parse $1
|
73
|
+
@updated ||= @created
|
74
|
+
@slug = $2
|
75
|
+
@output_dir = created.strftime('%Y/%m/%d')
|
76
|
+
@output_path = File.join output_dir, "#{slug}.html"
|
77
|
+
@uri = "/#{output_path}"
|
78
|
+
else
|
79
|
+
raise ArgumentError, "Bad file name: #{name}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Get the Post's unique identifier
|
85
|
+
def id
|
86
|
+
name.gsub /-/, '_'
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Get the Post's permalink
|
91
|
+
def permalink
|
92
|
+
return "#{uri}"
|
93
|
+
end
|
94
|
+
|
95
|
+
# def comments_link
|
96
|
+
# "#{uri}#comments"
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# def comment_count
|
100
|
+
# 0
|
101
|
+
# end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Formatted date the Post was published (i.e., "March 15, 2009")
|
105
|
+
def date
|
106
|
+
created.strftime '%B %e, %Y'
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# Year the Post was published as a String (i.e., "2009")
|
111
|
+
def year
|
112
|
+
created.strftime '%Y'
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# Month the Post was published as a String (i.e., "03")
|
117
|
+
def month
|
118
|
+
created.strftime '%m'
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# Day the Post was published as a String (i.e., "15")
|
123
|
+
def day
|
124
|
+
created.strftime '%d'
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# Does the post have a Time set?
|
129
|
+
def time?
|
130
|
+
false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module SemiStatic
|
2
|
+
class Posts
|
3
|
+
attr_reader :site, :posts, :names, :indices
|
4
|
+
|
5
|
+
def initialize(site)
|
6
|
+
@site = site
|
7
|
+
@posts = []
|
8
|
+
@names = {}
|
9
|
+
@indices = Set.new
|
10
|
+
end
|
11
|
+
|
12
|
+
NAME_RE = /^([0-9]{4})-([0-9]{2})-([0-9]{2})-(.*)/
|
13
|
+
|
14
|
+
def <<(source_path)
|
15
|
+
unless File.file?(source_path)
|
16
|
+
$stderr.puts "[#{source_path}]"
|
17
|
+
return
|
18
|
+
end
|
19
|
+
if source_path =~ NAME_RE
|
20
|
+
post = Post.new site, source_path
|
21
|
+
self.posts.push post
|
22
|
+
names[post.name] = post
|
23
|
+
indices << File.join(post.year, post.month, post.day)
|
24
|
+
indices << File.join(post.year, post.month)
|
25
|
+
indices << post.year
|
26
|
+
else
|
27
|
+
$stderr.puts "{#{source_path}}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def chop!(count)
|
32
|
+
raise ArgumentError unless count > 0
|
33
|
+
posts = self.posts.first(count)
|
34
|
+
@posts = []
|
35
|
+
@names = {}
|
36
|
+
@indices = Set.new
|
37
|
+
for post in posts
|
38
|
+
self.posts << post
|
39
|
+
names[post.name] = post
|
40
|
+
indices << File.join(post.year, post.month, post.day)
|
41
|
+
indices << File.join(post.year, post.month)
|
42
|
+
indices << post.year
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def length
|
47
|
+
self.posts.length
|
48
|
+
end
|
49
|
+
|
50
|
+
def first(n=nil)
|
51
|
+
if n.nil?
|
52
|
+
self.posts.first
|
53
|
+
else
|
54
|
+
self.posts.first(n)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def last(n=nil)
|
59
|
+
if n.nil?
|
60
|
+
self.posts.last
|
61
|
+
else
|
62
|
+
self.posts.last(n).reverse
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def from(year, month=nil, day=nil)
|
67
|
+
if year.is_a?(String) && month.nil? && day.nil?
|
68
|
+
date = year.split('/', 3)
|
69
|
+
year, month, day = date.collect { |c| c.to_i }
|
70
|
+
end
|
71
|
+
|
72
|
+
if month.nil?
|
73
|
+
from = Time.local year
|
74
|
+
to = Time.local(year + 1) - 1
|
75
|
+
elsif day.nil?
|
76
|
+
from = Time.local year, month
|
77
|
+
if month == 12
|
78
|
+
to = Time.local(year + 1, 1) - 1
|
79
|
+
else
|
80
|
+
to = Time.local(year, month + 1) - 1
|
81
|
+
end
|
82
|
+
else
|
83
|
+
from = Time.local year, month, day
|
84
|
+
to = Time.local year, month, day, 23, 59, 59
|
85
|
+
end
|
86
|
+
range = from..to
|
87
|
+
result = self.posts.select { |p| range.include?(p.created) }
|
88
|
+
class << result
|
89
|
+
alias_method :last_without_reverse, :last
|
90
|
+
def last_with_reverse(n=nil)
|
91
|
+
last_without_reverse(n).reverse
|
92
|
+
end
|
93
|
+
alias_method :last, :last_with_reverse
|
94
|
+
end
|
95
|
+
return result
|
96
|
+
end
|
97
|
+
|
98
|
+
def [](name)
|
99
|
+
return self.names[name]
|
100
|
+
end
|
101
|
+
|
102
|
+
def each(&block)
|
103
|
+
self.posts.each(&block)
|
104
|
+
end
|
105
|
+
|
106
|
+
def each_index(&block)
|
107
|
+
indices.each(&block)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|