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,54 @@
|
|
1
|
+
module SemiStatic
|
2
|
+
module Pygmentize
|
3
|
+
LEXER_FORMAT = /^[a-z]+$/i
|
4
|
+
|
5
|
+
def pygmentize(code, lang)
|
6
|
+
Pygmentize.pygmentize code, lang
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.pygmentize(code, lang)
|
10
|
+
unless lang =~ LEXER_FORMAT
|
11
|
+
raise ArgumentError, "invalid lexer: #{lang}"
|
12
|
+
end
|
13
|
+
|
14
|
+
Tempfile.open('semistatic-pygmentize') do |temp|
|
15
|
+
temp.write code
|
16
|
+
temp.close
|
17
|
+
|
18
|
+
cmd = "pygmentize -f html -l #{lang} #{temp.path}"
|
19
|
+
IO.popen(cmd) do |proc|
|
20
|
+
return proc.read
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
@@enabled = false
|
26
|
+
def self.enabled
|
27
|
+
@@enabled
|
28
|
+
end
|
29
|
+
def self.enabled=(value)
|
30
|
+
@@enabled = value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module MaRuKu #:nodoc:
|
36
|
+
module Out #:nodoc:
|
37
|
+
module HTML #:nodoc:
|
38
|
+
alias_method :to_html_code_without_pygments, :to_html_code
|
39
|
+
def to_html_code_with_pygments
|
40
|
+
if SemiStatic::Pygmentize.enabled
|
41
|
+
source = self.raw_code
|
42
|
+
lang = self.attributes[:lang] || 'text'
|
43
|
+
html = SemiStatic::Pygmentize.pygmentize source, lang
|
44
|
+
doc = Document.new html, :respect_whitespace => :all
|
45
|
+
|
46
|
+
add_ws doc.root
|
47
|
+
else
|
48
|
+
to_html_code_without_pygments
|
49
|
+
end
|
50
|
+
end
|
51
|
+
alias_method :to_html_code, :to_html_code_with_pygments
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
module SemiStatic
|
2
|
+
class Site
|
3
|
+
attr_accessor :clean_first, :check_mtime, :quick_mode, :show_statistics
|
4
|
+
|
5
|
+
attr_reader :time
|
6
|
+
attr_reader :source_dir, :layouts, :pages, :posts, :snippets, :categories, :tags
|
7
|
+
attr_reader :year_index, :month_index, :day_index
|
8
|
+
attr_reader :metadata, :stylesheets
|
9
|
+
|
10
|
+
attr_reader :stats
|
11
|
+
|
12
|
+
def initialize(source_dir)
|
13
|
+
@clean_first = false
|
14
|
+
@first_pass = true
|
15
|
+
@quick_mode = false
|
16
|
+
@check_mtime = false
|
17
|
+
@show_statistics = false
|
18
|
+
@source_dir = source_dir
|
19
|
+
@time = Time.now
|
20
|
+
@stats = Statistics.new
|
21
|
+
stats.record(:site, :load) { load }
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.open(source_dir)
|
25
|
+
raise ArugmentError, "block required" unless block_given?
|
26
|
+
site = SemiStatic::Site.new source_dir
|
27
|
+
yield site
|
28
|
+
end
|
29
|
+
|
30
|
+
def output(path)
|
31
|
+
if @first_pass
|
32
|
+
@first_pass = false
|
33
|
+
FileUtils.rm_rf path if clean_first
|
34
|
+
end
|
35
|
+
FileUtils.mkdir_p path
|
36
|
+
|
37
|
+
if quick_mode
|
38
|
+
posts.chop! 20
|
39
|
+
end
|
40
|
+
@stats.reset
|
41
|
+
|
42
|
+
unless metadata.nil? || !metadata['static'].is_a?(Array)
|
43
|
+
stats.record(:site, :static) do
|
44
|
+
for dir in metadata['static']
|
45
|
+
FileUtils.cp_r File.join(source_dir, dir), File.join(path, dir)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
before = Time.now
|
51
|
+
Dir.chdir(path) do
|
52
|
+
stats.record(:site, :pages) do
|
53
|
+
pages.each do |name, page|
|
54
|
+
FileUtils.mkdir_p page.output_dir unless File.directory?(page.output_dir)
|
55
|
+
if check_mtime
|
56
|
+
if File.file?(page.output_path) && File.mtime(page.output_path) > page.source_mtime
|
57
|
+
next
|
58
|
+
end
|
59
|
+
page.load
|
60
|
+
end
|
61
|
+
File.open(page.output_path, 'w') { |f| f.write page.render }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
stats.record(:site, :posts) do
|
66
|
+
posts.each do |post|
|
67
|
+
FileUtils.mkdir_p post.output_dir unless File.directory?(post.output_dir)
|
68
|
+
if check_mtime
|
69
|
+
if File.file?(post.output_path) && File.mtime(post.output_path) > post.source_mtime
|
70
|
+
next
|
71
|
+
end
|
72
|
+
post.load
|
73
|
+
end
|
74
|
+
File.open(post.output_path, 'w') { |f| f.write post.render }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
stats.record(:site, :stylesheets) do
|
79
|
+
unless stylesheets.nil?
|
80
|
+
stylesheets.each do |name, stylesheet|
|
81
|
+
FileUtils.mkdir_p stylesheet.output_dir unless File.directory?(stylesheet.output_dir)
|
82
|
+
if check_mtime
|
83
|
+
if File.file?(stylesheet.output_path) && File.mtime(stylesheet.output_path) > stylesheet.source_mtime
|
84
|
+
next
|
85
|
+
end
|
86
|
+
stylesheet.load
|
87
|
+
end
|
88
|
+
File.open(stylesheet.output_path, 'w') { |f| f.write stylesheet.render }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
stats.record(:site, :indices) do
|
94
|
+
unless year_index.nil? && month_index.nil? && day_index.nil?
|
95
|
+
posts.each_index do |dir|
|
96
|
+
posts = self.posts.from(dir)
|
97
|
+
Dir.chdir(dir) do
|
98
|
+
context = dir.split('/').collect { |c| c.to_i }
|
99
|
+
date_index = case context.length
|
100
|
+
when 1
|
101
|
+
year_index
|
102
|
+
when 2
|
103
|
+
month_index
|
104
|
+
when 3
|
105
|
+
day_index
|
106
|
+
else
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
date_index.posts = posts
|
110
|
+
date_index.context = Time.local *context
|
111
|
+
File.open('index.html', 'w') { |f| f.write date_index.render }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
self.stats.display if show_statistics
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
def with_source_files(subdir, pattern)
|
123
|
+
raise ArgumentError unless block_given?
|
124
|
+
Dir.chdir(File.join(source_dir, subdir)) do
|
125
|
+
Dir.glob(pattern) do |path|
|
126
|
+
yield path
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def config_file_path
|
132
|
+
File.join source_dir, 'semi.yml'
|
133
|
+
end
|
134
|
+
|
135
|
+
def load
|
136
|
+
if File.file?(config_file_path)
|
137
|
+
@metadata = File.open(config_file_path) { |io| YAML.load io }
|
138
|
+
end
|
139
|
+
load_stylesheets
|
140
|
+
load_layouts
|
141
|
+
load_pages
|
142
|
+
load_posts
|
143
|
+
load_snippets
|
144
|
+
load_indices
|
145
|
+
end
|
146
|
+
|
147
|
+
def load_stylesheets
|
148
|
+
unless metadata.nil? || metadata['stylesheets'].nil?
|
149
|
+
Dir.chdir(File.join(source_dir, 'stylesheets')) do
|
150
|
+
config = metadata['stylesheets']
|
151
|
+
if config.is_a?(Array)
|
152
|
+
config = config.inject({}) { |hash,name| hash[name] = {}; hash }
|
153
|
+
end
|
154
|
+
@stylesheets = config.inject({}) do |hash,pair|
|
155
|
+
name, opts = pair
|
156
|
+
hash[name.to_sym] = Stylesheet.new self, name, opts
|
157
|
+
hash
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def load_layouts
|
164
|
+
@layouts = Hash.new
|
165
|
+
with_source_files('layouts', '*.{haml,erb}') do |path|
|
166
|
+
next unless File.file?(path)
|
167
|
+
|
168
|
+
file = File.basename(path)
|
169
|
+
if file[0..0] != '_'
|
170
|
+
layout = Layout.new self, path
|
171
|
+
self.layouts[layout.name.to_sym] = layout
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def load_pages
|
177
|
+
@pages = Hash.new
|
178
|
+
with_source_files('pages', '**/*.{html,haml,erb,txt,md,markdown}') do |path|
|
179
|
+
next if File.directory?(path)
|
180
|
+
next unless path.split('/').grep(/^_/).empty?
|
181
|
+
|
182
|
+
page = Page.new self, path
|
183
|
+
self.pages[page.name] = page
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def load_posts
|
188
|
+
@posts = Posts.new(self)
|
189
|
+
@categories = Categories.new
|
190
|
+
@tags = Categories.new
|
191
|
+
with_source_files('posts', '*.{html,haml,erb,txt,md,markdown}') do |path|
|
192
|
+
posts << path
|
193
|
+
end
|
194
|
+
posts.posts.sort! { |l,r| l.created <=> r.created }
|
195
|
+
end
|
196
|
+
|
197
|
+
def load_snippets
|
198
|
+
return unless File.directory?(File.join(source_dir, 'snippets'))
|
199
|
+
|
200
|
+
@snippets = Hash.new
|
201
|
+
with_source_files('snippets', '*.{html,haml,erb,txt,md,markdown}') do |path|
|
202
|
+
next unless File.file?(path)
|
203
|
+
next unless ('a'..'z').include?(path[0..0].downcase)
|
204
|
+
|
205
|
+
snippet = Snippet.new self, path
|
206
|
+
self.snippets[snippet.name] = snippet
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def load_indices
|
211
|
+
return unless File.directory?(File.join(source_dir, 'indices'))
|
212
|
+
|
213
|
+
with_source_files('indices', '{year,month,day}.{haml,erb}') do |path|
|
214
|
+
# puts path
|
215
|
+
next unless File.file?(path)
|
216
|
+
|
217
|
+
file = File.basename(path)
|
218
|
+
name = File.basename(file, File.extname(file))
|
219
|
+
case name
|
220
|
+
when 'year'
|
221
|
+
@year_index = Index.new self, path
|
222
|
+
when 'month'
|
223
|
+
@month_index = Index.new self, path
|
224
|
+
when 'day'
|
225
|
+
@day_index = Index.new self, path
|
226
|
+
else
|
227
|
+
raise ArgumentError, "Unexpected index file: #{path}"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module SemiStatic
|
2
|
+
class Statistics
|
3
|
+
def initialize
|
4
|
+
self.reset
|
5
|
+
end
|
6
|
+
|
7
|
+
def reset
|
8
|
+
@data = Hash.new { |hash,key| hash[key] = Hash.new }
|
9
|
+
end
|
10
|
+
|
11
|
+
def record(category, item)
|
12
|
+
raise ArgumentError unless block_given?
|
13
|
+
before = Time.now
|
14
|
+
result = yield
|
15
|
+
@data[category][item] = Time.now - before
|
16
|
+
return result
|
17
|
+
end
|
18
|
+
|
19
|
+
def display
|
20
|
+
# details = {}
|
21
|
+
@data.each do |category,items|
|
22
|
+
next if category == :site
|
23
|
+
sum = 0; items.values.each { |time| sum += time }
|
24
|
+
list = items.sort { |l,r| l.last <=> r.last }
|
25
|
+
|
26
|
+
if list.length > 1
|
27
|
+
printf "%10s c:%-3d sum:%9.6f min:%.6f max:%.6f avg:%.6f\n",
|
28
|
+
category, items.length, sum, list.first.last,
|
29
|
+
list.last.last, sum / items.length
|
30
|
+
# details[category] = list.reverse.first(5).collect { |pair| { pair.first => pair.last } }
|
31
|
+
else
|
32
|
+
printf "%10s c:%-3d sum:%9.6f\n", category, items.length, sum
|
33
|
+
end
|
34
|
+
end
|
35
|
+
puts '---'
|
36
|
+
@data[:site].each { |cat,time| printf "%15s %9.6f\n", cat.to_s.capitalize, time }
|
37
|
+
|
38
|
+
# unless details.empty?
|
39
|
+
# puts '---'
|
40
|
+
# puts
|
41
|
+
# puts details.to_yaml
|
42
|
+
# end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module SemiStatic
|
2
|
+
class Stylesheet < Base
|
3
|
+
attr_reader :name, :options, :output_dir, :output_path
|
4
|
+
|
5
|
+
def initialize(site, name, options={})
|
6
|
+
path = "#{name}.sass"
|
7
|
+
path = "#{name}.css" unless File.file?(path)
|
8
|
+
super(site, path)
|
9
|
+
|
10
|
+
@name, @options = name, options
|
11
|
+
@output_dir = 'css'
|
12
|
+
@output_path = "#{output_dir}/#{name}.css"
|
13
|
+
end
|
14
|
+
|
15
|
+
def load
|
16
|
+
super
|
17
|
+
Dir.chdir(File.dirname(full_source_path)) do
|
18
|
+
@content = case source_ext
|
19
|
+
when '.sass'
|
20
|
+
Sass::Engine.new(source_content, :filename => source_path).render
|
21
|
+
when '.css'
|
22
|
+
source_content
|
23
|
+
else
|
24
|
+
raise ArgumentError, "Unsupported format: #{self.source_path}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def render
|
30
|
+
@content
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
$VERBOSE = false # Haml creates a lot of noise with this set
|
2
|
+
|
3
|
+
require 'tempfile'
|
4
|
+
require 'test/unit'
|
5
|
+
require "#{File.dirname __FILE__}/../lib/semi-static"
|
6
|
+
|
7
|
+
class Test::Unit::TestCase
|
8
|
+
TEST_SOURCE_DIR = File.join(File.dirname(__FILE__), 'source')
|
9
|
+
TEST_OUTPUT_DIR = File.join(File.dirname(__FILE__), 'output')
|
10
|
+
|
11
|
+
def with_test_site
|
12
|
+
raise ArgumentError, "block required" unless block_given?
|
13
|
+
site = SemiStatic::Site.open(TEST_SOURCE_DIR) do |site|
|
14
|
+
assert_not_nil site
|
15
|
+
yield site
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def with_test_cli
|
20
|
+
raise ArgumentError, "block required" unless block_given?
|
21
|
+
cli = SemiStatic::CLI.new
|
22
|
+
cli.source_dir = TEST_SOURCE_DIR
|
23
|
+
cli.output_dir = TEST_OUTPUT_DIR
|
24
|
+
yield cli
|
25
|
+
end
|
26
|
+
|
27
|
+
def with_test_site_page(page_name)
|
28
|
+
raise ArgumentError, "block required" unless block_given?
|
29
|
+
with_test_site do |site|
|
30
|
+
page = site.pages[page_name]
|
31
|
+
assert_not_nil page
|
32
|
+
yield site, page
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def with_test_site_post(post_name)
|
37
|
+
raise ArgumentError, "block required" unless block_given?
|
38
|
+
with_test_site do |site|
|
39
|
+
post = site.posts[post_name]
|
40
|
+
assert_not_nil post
|
41
|
+
yield site, post
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def ref(name)
|
46
|
+
path = File.join File.dirname(__FILE__), 'ref', name
|
47
|
+
return File.read(path)
|
48
|
+
end
|
49
|
+
|
50
|
+
def out(path)
|
51
|
+
path = File.join File.dirname(__FILE__), 'output', path
|
52
|
+
return File.read(path)
|
53
|
+
end
|
54
|
+
|
55
|
+
def diff(left, right, options={})
|
56
|
+
Tempfile.open('left') do |t1|
|
57
|
+
t1.write left
|
58
|
+
t1.close
|
59
|
+
Tempfile.open('right') do |t2|
|
60
|
+
t2.write right
|
61
|
+
t2.close
|
62
|
+
|
63
|
+
cmd = 'diff -ub -I \'^[[:space:]]*$\''
|
64
|
+
|
65
|
+
cmd << " --label '#{options[:left]}'" if options.include?(:left)
|
66
|
+
cmd << " #{t1.path}"
|
67
|
+
|
68
|
+
cmd << " --label '#{options[:right]}'" if options.include?(:right)
|
69
|
+
cmd << " #{t2.path}"
|
70
|
+
|
71
|
+
return `#{cmd}`
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def assert_equal_diff(expected, actual, diff_options={})
|
77
|
+
msg = diff expected, actual, diff_options
|
78
|
+
assert_block(msg) { expected == actual }
|
79
|
+
end
|
80
|
+
|
81
|
+
def assert_render_equal_ref(ref_name, renderable, render_options={}, diff_options={})
|
82
|
+
expected = ref(ref_name)
|
83
|
+
actual = renderable.render(render_options)
|
84
|
+
diff_options = { :left => ref_name, :right => renderable.name }.merge diff_options
|
85
|
+
|
86
|
+
# diff_options[:left] = ref_name unless diff_options.include?(:left)
|
87
|
+
# diff_options[:right] = renderable.output_path unless diff_options.include?(:right)
|
88
|
+
|
89
|
+
msg = diff expected, actual, diff_options
|
90
|
+
assert_block(msg) { $?.success? }
|
91
|
+
end
|
92
|
+
|
93
|
+
def assert_directory(path, msg=nil)
|
94
|
+
msg = build_message msg, "Expected to be a directory: <?>", path
|
95
|
+
assert_block(msg) { File.directory? path }
|
96
|
+
end
|
97
|
+
|
98
|
+
def assert_file(path, msg=nil)
|
99
|
+
msg = build_message msg, "Expected to be a regular file: <?>", path
|
100
|
+
assert_block(msg) { File.file? path }
|
101
|
+
end
|
102
|
+
|
103
|
+
def assert_file_equal_ref(ref_name, out_path, diff_options={})
|
104
|
+
expected = ref(ref_name)
|
105
|
+
actual = out(out_path)
|
106
|
+
diff_options = { :left => ref_name, :right => out_path }.merge diff_options
|
107
|
+
|
108
|
+
msg = diff expected, actual, diff_options
|
109
|
+
assert_block(msg) { $?.success? }
|
110
|
+
end
|
111
|
+
end
|