henshin 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -1
- data/README.markdown +9 -46
- data/Rakefile +2 -1
- data/VERSION +1 -1
- data/bin/henshin +11 -12
- data/henshin.gemspec +20 -27
- data/lib/henshin.rb +21 -98
- data/lib/henshin/archive.rb +87 -115
- data/{bin → lib/henshin/exec}/files.rb +0 -0
- data/lib/henshin/ext.rb +35 -58
- data/lib/henshin/gen.rb +83 -68
- data/lib/henshin/labels.rb +144 -0
- data/lib/henshin/plugin.rb +70 -33
- data/lib/henshin/plugins/highlight.rb +19 -26
- data/lib/henshin/plugins/liquid.rb +50 -52
- data/lib/henshin/plugins/maruku.rb +14 -16
- data/lib/henshin/plugins/sass.rb +19 -23
- data/lib/henshin/plugins/textile.rb +14 -16
- data/lib/henshin/post.rb +67 -120
- data/lib/henshin/site.rb +154 -131
- data/lib/henshin/static.rb +10 -8
- data/test/helper.rb +20 -8
- data/test/site/css/{includes/reset.sass → _reset.sass} +0 -0
- data/test/site/css/screen.sass +1 -1
- data/test/site/index.html +2 -3
- data/test/site/layouts/archive_date.html +5 -4
- data/test/site/layouts/archive_month.html +10 -5
- data/test/site/layouts/archive_year.html +13 -6
- data/test/site/layouts/category_page.html +7 -7
- data/test/site/layouts/main.html +3 -4
- data/test/site/layouts/post.html +1 -1
- data/test/site/layouts/tag_index.html +3 -2
- data/test/site/layouts/tag_page.html +6 -6
- data/test/site/options.yaml +2 -2
- data/test/site/plugins/test.rb +1 -3
- data/test/site/posts/Testing-Stuff.markdown +1 -1
- data/test/site/posts/Textile-Test.textile +1 -1
- data/test/site/posts/lorem-ipsum.markdown +1 -1
- data/test/site/posts/same-date.markdown +1 -1
- data/test/{test_henshin.rb → suite.rb} +0 -1
- data/test/test_gen.rb +98 -0
- data/test/test_options.rb +34 -19
- data/test/test_post.rb +67 -0
- data/test/test_site.rb +159 -46
- data/test/test_static.rb +13 -0
- metadata +53 -32
- data/lib/henshin/categories.rb +0 -29
- data/lib/henshin/tags.rb +0 -28
- data/test/test_archives.rb +0 -27
- data/test/test_categories.rb +0 -0
- data/test/test_gens.rb +0 -54
- data/test/test_layouts.rb +0 -14
- data/test/test_posts.rb +0 -75
- data/test/test_statics.rb +0 -0
- data/test/test_tags.rb +0 -0
- data/test/text_exts.rb +0 -0
data/lib/henshin/site.rb
CHANGED
@@ -1,31 +1,120 @@
|
|
1
1
|
module Henshin
|
2
2
|
|
3
3
|
class Site
|
4
|
-
|
5
|
-
attr_accessor :posts, :gens, :statics, :archive, :tags, :categories
|
6
|
-
attr_accessor :layouts, :config
|
7
4
|
|
8
|
-
|
5
|
+
# @return [Hash] the configuration made up of the override, options.yaml and Henshin::Defaults
|
6
|
+
attr_accessor :config
|
7
|
+
|
8
|
+
# @return [Pathname] the path to the site to generate from where the command is executed
|
9
|
+
attr_accessor :root
|
10
|
+
|
11
|
+
# @return [Pathname] the path to where the finished site should be written
|
12
|
+
attr_accessor :target
|
13
|
+
|
14
|
+
# @return [String] a path which should be prepended to all urls
|
15
|
+
attr_accessor :base
|
16
|
+
|
17
|
+
# @return [Array]
|
18
|
+
attr_accessor :gens, :posts, :statics
|
19
|
+
|
20
|
+
# @return [Hash{String => String}]
|
21
|
+
attr_accessor :layouts
|
22
|
+
|
23
|
+
# @return [Tags]
|
24
|
+
attr_accessor :tags
|
25
|
+
|
26
|
+
# @return [Categories]
|
27
|
+
attr_accessor :categories
|
28
|
+
|
29
|
+
# @return [Archive]
|
30
|
+
attr_accessor :archive
|
31
|
+
|
32
|
+
# @return [Hash{String => Plugin}]
|
33
|
+
attr_accessor :plugins
|
34
|
+
|
35
|
+
# A new instance of site
|
36
|
+
#
|
37
|
+
# @param [Hash{String => Object}] override data to override loaded options
|
38
|
+
# @return [self]
|
39
|
+
def initialize(override={})
|
9
40
|
self.reset
|
10
|
-
|
41
|
+
self.configure(override)
|
42
|
+
self.load_plugins
|
43
|
+
self
|
11
44
|
end
|
12
45
|
|
13
|
-
# Resets
|
14
|
-
|
15
|
-
|
16
|
-
|
46
|
+
# Resets all instance variables, except +config+ and +plugins+ as these shouldn't
|
47
|
+
# need to be reloaded each time.
|
48
|
+
#
|
49
|
+
# @return [self]
|
50
|
+
def reset
|
51
|
+
@gens = []
|
52
|
+
@posts = []
|
17
53
|
@statics = []
|
54
|
+
@layouts = {}
|
18
55
|
|
19
|
-
@archive
|
20
|
-
@tags
|
21
|
-
@categories =
|
56
|
+
@archive = Archive.new(self)
|
57
|
+
@tags = Labels.new('tag', self)
|
58
|
+
@categories = Labels.new('category', self)
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Creates the configuration hash by merging defaults, supplied options and options
|
63
|
+
# read from the 'options.yaml' file. Then sets root, target, base and appends
|
64
|
+
# special directories to @config['exclude']
|
65
|
+
#
|
66
|
+
# @param [Hash] override to override other set options
|
67
|
+
def configure(override)
|
68
|
+
@config = {}
|
69
|
+
config_file = File.join((override['root'] || Defaults['root']), '/options.yaml')
|
22
70
|
|
23
|
-
|
71
|
+
# change target to represent given root, only if root given
|
72
|
+
if override['root'] && !override['target']
|
73
|
+
override['target'] = File.join(override['root'], Defaults['target'])
|
74
|
+
end
|
75
|
+
|
76
|
+
begin
|
77
|
+
config = YAML.load_file(config_file)
|
78
|
+
@config = Defaults.merge(config).merge(override)
|
79
|
+
rescue => e
|
80
|
+
$stderr.puts "\nCould not read configuration, falling back to defaults..."
|
81
|
+
$stderr.puts "-> #{e.to_s}"
|
82
|
+
@config = Defaults.merge(override)
|
83
|
+
end
|
84
|
+
@root = @config['root'].to_p
|
85
|
+
@target = @config['target'].to_p
|
86
|
+
|
87
|
+
@base = @config['base'] || "/"
|
88
|
+
@base = '/' + @base unless @base[0] == '/' # need to make sure it starts with slash
|
89
|
+
|
90
|
+
@config['exclude'] << '/_site' << '/plugins'
|
24
91
|
end
|
25
92
|
|
93
|
+
# Requires each plugin in @config['plugins'], then loads and sorts them into
|
94
|
+
# +plugins+ by type
|
95
|
+
def load_plugins
|
96
|
+
@plugins = {:generators => {}, :layoutors => []}
|
97
|
+
|
98
|
+
@config['plugins'].each do |plugin|
|
99
|
+
begin
|
100
|
+
require File.join('henshin', 'plugins', plugin)
|
101
|
+
rescue LoadError
|
102
|
+
require File.join(@root, 'plugins', plugin)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
Henshin::Generator.subclasses.each do |plugin|
|
107
|
+
plugin = plugin.new(self)
|
108
|
+
plugin.extensions[:input].each do |ext|
|
109
|
+
@plugins[:generators][ext] = plugin
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
@plugins[:layoutors] = Henshin::Layoutor.subclasses.map {|l| l.new(self)}.sort
|
114
|
+
end
|
26
115
|
|
27
116
|
##
|
28
|
-
# Read, process, render and write
|
117
|
+
# Read, process, render and write
|
29
118
|
def build
|
30
119
|
self.reset
|
31
120
|
self.read
|
@@ -37,45 +126,48 @@ module Henshin
|
|
37
126
|
|
38
127
|
##
|
39
128
|
# Reads all necessary files and puts them into the necessary arrays
|
129
|
+
#
|
130
|
+
# @return [self]
|
40
131
|
def read
|
41
132
|
self.read_layouts
|
42
133
|
self.read_posts
|
43
134
|
self.read_gens
|
44
135
|
self.read_statics
|
136
|
+
self
|
45
137
|
end
|
46
138
|
|
47
|
-
# Adds all items in 'layouts' to the layouts array
|
139
|
+
# Adds all items in '/layouts' to the layouts array
|
48
140
|
def read_layouts
|
49
|
-
path = File.join(@
|
141
|
+
path = File.join(@root, 'layouts')
|
50
142
|
Dir.glob(path + '/*.*').each do |layout|
|
51
143
|
layout =~ /([a-zA-Z0-9 _-]+)\.([a-zA-Z0-9-]+)/
|
52
144
|
@layouts[$1] = File.open(layout, 'r') {|f| f.read}
|
53
145
|
end
|
54
146
|
end
|
55
147
|
|
56
|
-
# Adds all items in 'posts' to the posts array
|
148
|
+
# Adds all items in '/posts' to the posts array
|
57
149
|
def read_posts
|
58
|
-
path = File.join(@
|
150
|
+
path = File.join(@root, 'posts')
|
59
151
|
Dir.glob(path + '/**/*.*').each do |post|
|
60
|
-
@posts << Post.new(post, self)
|
152
|
+
@posts << Post.new(post.to_p, self).read
|
61
153
|
end
|
62
154
|
end
|
63
155
|
|
64
156
|
# Adds all files that need to be run through a plugin in an array
|
65
157
|
def read_gens
|
66
|
-
files = Dir.glob( File.join(@
|
158
|
+
files = Dir.glob( File.join(@root, '**', '*.*') )
|
67
159
|
gens = files.select {|i| gen?(i) }
|
68
|
-
gens.each do |
|
69
|
-
@gens << Gen.new(
|
160
|
+
gens.each do |gen|
|
161
|
+
@gens << Gen.new(gen.to_p, self).read
|
70
162
|
end
|
71
163
|
end
|
72
164
|
|
73
165
|
# Adds all static files to an array
|
74
166
|
def read_statics
|
75
|
-
files = Dir.glob( File.join(@
|
167
|
+
files = Dir.glob( File.join(@root, '**', '*.*') )
|
76
168
|
static = files.select {|i| static?(i) }
|
77
|
-
static.each do |
|
78
|
-
@statics << Static.new(
|
169
|
+
static.each do |static|
|
170
|
+
@statics << Static.new(static.to_p, self)
|
79
171
|
end
|
80
172
|
end
|
81
173
|
|
@@ -83,153 +175,84 @@ module Henshin
|
|
83
175
|
##
|
84
176
|
# Processes all of the necessary files
|
85
177
|
def process
|
86
|
-
@posts.each_parallel {|p| p.process}
|
87
178
|
@posts.sort!
|
88
|
-
@gens.
|
179
|
+
@gens.sort!
|
89
180
|
|
90
|
-
|
91
|
-
|
92
|
-
|
181
|
+
@posts.each do |post|
|
182
|
+
@tags << post if post.data['tags']
|
183
|
+
@categories << post if post.data['category']
|
184
|
+
@archive << post
|
185
|
+
end
|
186
|
+
self
|
93
187
|
end
|
94
188
|
|
95
189
|
# @return [Hash] the payload for the layout engine
|
96
190
|
def payload
|
97
|
-
{
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
'posts' => @posts.collect{|i| i.to_hash},
|
105
|
-
'tags' => @tags.collect {|k, t| t.to_hash},
|
106
|
-
'categories' => @categories.collect {|k, t| t.to_hash},
|
107
|
-
'archive' => @archive.to_hash
|
108
|
-
}
|
109
|
-
}
|
110
|
-
end
|
111
|
-
|
112
|
-
# Creates tags from posts and adds them to @tags
|
113
|
-
def build_tags
|
114
|
-
@posts.each do |p|
|
115
|
-
p.tags.each do |t|
|
116
|
-
@tags[t].posts << p
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
# Create categories from posts and add to @categories
|
122
|
-
def build_categories
|
123
|
-
@posts.each do |p|
|
124
|
-
@categories[p.category].posts << p unless p.category.nil?
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
# @return [Hash] archive hash
|
129
|
-
def build_archive
|
130
|
-
@posts.each {|p| @archive.add_post(p)}
|
191
|
+
r = {'site' => @config}
|
192
|
+
r['site']['created_at'] = Time.now
|
193
|
+
r['site']['posts'] = @posts.collect{|i| i.to_hash}
|
194
|
+
r['site']['tags'] = @tags.to_hash
|
195
|
+
r['site']['categories'] = @categories.to_hash
|
196
|
+
r['site']['archive'] = @archive.to_hash
|
197
|
+
r
|
131
198
|
end
|
132
199
|
|
133
200
|
##
|
134
201
|
# Renders the files
|
135
202
|
def render
|
136
|
-
@posts.
|
137
|
-
@gens.each {|
|
203
|
+
@posts.each {|post| post.render}
|
204
|
+
@gens.each {|gen| gen.render}
|
205
|
+
|
206
|
+
self
|
138
207
|
end
|
139
208
|
|
140
209
|
|
141
210
|
##
|
142
211
|
# Writes the files
|
143
212
|
def write
|
144
|
-
@posts.
|
145
|
-
@gens.
|
146
|
-
@statics.
|
213
|
+
@posts.each {|post| post.write}
|
214
|
+
@gens.each {|gen| gen.write}
|
215
|
+
@statics.each {|static| static.write}
|
147
216
|
|
148
217
|
@archive.write
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
# Writes the necessary pages for tags, but only if the correct layouts are present
|
154
|
-
def write_tags
|
155
|
-
if @layouts['tag_index']
|
156
|
-
write_path = File.join( @config[:root], 'tags', 'index.html' )
|
157
|
-
|
158
|
-
tag_index = Gen.new(write_path, self)
|
159
|
-
tag_index.layout = @layouts['tag_index']
|
160
|
-
|
161
|
-
tag_index.render
|
162
|
-
tag_index.write
|
163
|
-
end
|
164
|
-
|
165
|
-
if @layouts['tag_page']
|
166
|
-
@tags.each do |n, tag|
|
167
|
-
write_path = File.join( @config[:root], 'tags', tag.name, 'index.html' )
|
168
|
-
|
169
|
-
payload = {:name => 'tag', :payload => tag.to_hash}
|
170
|
-
tag_page = Gen.new(write_path, self, payload)
|
171
|
-
tag_page.layout = @layouts['tag_page']
|
172
|
-
|
173
|
-
tag_page.render
|
174
|
-
tag_page.write
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
# Writes the necessary pages for categories, but only if the correct layouts are present
|
180
|
-
def write_categories
|
181
|
-
if @layouts['category_index']
|
182
|
-
write_path = File.join( @config[:root], 'categories', 'index.html' )
|
183
|
-
|
184
|
-
category_index = Gen.new(write_path, self)
|
185
|
-
category_index.layout = @layouts['category_index']
|
186
|
-
|
187
|
-
category_index.render
|
188
|
-
category_index.write
|
189
|
-
end
|
190
|
-
|
191
|
-
if @layouts['category_page']
|
192
|
-
@categories.each do |n, category|
|
193
|
-
write_path = File.join( @config[:root], 'categories', category.name, 'index.html' )
|
194
|
-
|
195
|
-
payload = {:name => 'category', :payload => category.to_hash}
|
196
|
-
category_page = Gen.new(write_path, self, payload)
|
197
|
-
category_page.layout = @layouts['category_page']
|
198
|
-
|
199
|
-
category_page.render
|
200
|
-
category_page.write
|
201
|
-
end
|
202
|
-
end
|
218
|
+
@tags.write
|
219
|
+
@categories.write
|
220
|
+
self
|
203
221
|
end
|
204
222
|
|
205
223
|
|
206
|
-
# @
|
224
|
+
# @param [String] path to test
|
225
|
+
# @return [Bool] whether the path points to a static
|
207
226
|
def static?( path )
|
208
227
|
!( layout?(path) || post?(path) || gen?(path) || ignored?(path) )
|
209
228
|
end
|
210
229
|
|
211
|
-
# @
|
230
|
+
# @param [String] path to test
|
231
|
+
# @return [Bool] whether the path points to a layout
|
212
232
|
def layout?( path )
|
213
233
|
path.include?('layouts/') && !ignored?(path)
|
214
234
|
end
|
215
235
|
|
216
|
-
# @
|
236
|
+
# @param [String] path to test
|
237
|
+
# @return [Bool] whether the path points to a post
|
217
238
|
def post?( path )
|
218
239
|
path.include?('posts/') && !ignored?(path)
|
219
240
|
end
|
220
241
|
|
221
|
-
# @
|
242
|
+
# @param [String] path to test
|
243
|
+
# @return [Bool] whether the path points to a gen
|
222
244
|
def gen?( path )
|
223
245
|
return false if post?(path) || layout?(path) || ignored?(path)
|
224
|
-
return true if @
|
246
|
+
return true if @plugins[:generators].has_key? path.to_p.extname[1..-1]
|
225
247
|
return true if File.open(path, "r").read(3) == "---"
|
226
248
|
false
|
227
249
|
end
|
228
250
|
|
229
|
-
# @
|
251
|
+
# @param [String] path to test
|
252
|
+
# @return [Bool] whether the path points to a file which should be ignored
|
230
253
|
def ignored?( path )
|
231
|
-
ignored = ['/options.yaml'] + @config[
|
232
|
-
ignored.collect! {|i| File.join(@
|
254
|
+
ignored = ['/options.yaml'] + @config['exclude']
|
255
|
+
ignored.collect! {|i| File.join(@root, i)}
|
233
256
|
ignored.each do |i|
|
234
257
|
return true if path.include? i
|
235
258
|
end
|
data/lib/henshin/static.rb
CHANGED
@@ -2,25 +2,27 @@ module Henshin
|
|
2
2
|
|
3
3
|
class Static
|
4
4
|
|
5
|
-
attr_accessor :path, :site, :
|
5
|
+
attr_accessor :path, :site, :content
|
6
6
|
|
7
7
|
def initialize( path, site )
|
8
8
|
@path = path
|
9
9
|
@site = site
|
10
|
-
@
|
11
|
-
@content = File.read( path )
|
10
|
+
@content = File.read(path)
|
12
11
|
end
|
13
12
|
|
14
13
|
##
|
15
14
|
# Writes the file to the correct place
|
16
15
|
def write
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
file = File.new( File.join( write_path ), "w" )
|
21
|
-
file.puts( @content )
|
16
|
+
FileUtils.mkdir_p(self.write_path.dirname)
|
17
|
+
file = File.new(self.write_path, "w")
|
18
|
+
file.puts(@content)
|
22
19
|
end
|
23
20
|
|
21
|
+
# @return [Pathname] path to write the file
|
22
|
+
def write_path
|
23
|
+
rel = @path.relative_path_from(@site.root)
|
24
|
+
@site.target + File.join(@site.base, rel)[1..-1]
|
25
|
+
end
|
24
26
|
|
25
27
|
def inspect
|
26
28
|
"#<Static:#{@path}>"
|
data/test/helper.rb
CHANGED
@@ -1,22 +1,23 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'test/unit'
|
3
2
|
require 'shoulda'
|
4
3
|
require 'rr'
|
5
4
|
|
6
|
-
|
7
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
|
-
require 'henshin'
|
5
|
+
require File.dirname(__FILE__) + '/../lib/henshin'
|
9
6
|
|
10
7
|
class Test::Unit::TestCase
|
11
8
|
|
12
9
|
include RR::Adapters::TestUnit
|
13
10
|
|
11
|
+
def site_override
|
12
|
+
{'root' => root_dir, 'target' => target_dir}
|
13
|
+
end
|
14
|
+
|
14
15
|
def root_dir
|
15
16
|
File.join(File.dirname(__FILE__), 'site')
|
16
17
|
end
|
17
18
|
|
18
19
|
def target_dir
|
19
|
-
"_site"
|
20
|
+
File.join(root_dir, "_site")
|
20
21
|
end
|
21
22
|
|
22
23
|
def remove_site
|
@@ -24,9 +25,20 @@ class Test::Unit::TestCase
|
|
24
25
|
end
|
25
26
|
|
26
27
|
def new_site
|
27
|
-
|
28
|
-
|
29
|
-
|
28
|
+
Henshin::Site.new(site_override)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
# Determines whether +instance+ is a +klass+ using #is_a?.
|
34
|
+
# This is similar to assert_instance_of except it doesn't
|
35
|
+
# have to be an instance of +klass+ just an instance of a
|
36
|
+
# decendant of +klass+.
|
37
|
+
#
|
38
|
+
# @param [Class] klass the klass to test for
|
39
|
+
# @param [Object] instance of the object to test
|
40
|
+
def assert_is_a(klass, instance)
|
41
|
+
assert instance.is_a?(klass), "Expected #{instance} to be a #{klass}"
|
30
42
|
end
|
31
43
|
|
32
44
|
end
|