henshin 0.3.0 → 0.4.0
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/.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
|