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.
Files changed (56) hide show
  1. data/.gitignore +5 -1
  2. data/README.markdown +9 -46
  3. data/Rakefile +2 -1
  4. data/VERSION +1 -1
  5. data/bin/henshin +11 -12
  6. data/henshin.gemspec +20 -27
  7. data/lib/henshin.rb +21 -98
  8. data/lib/henshin/archive.rb +87 -115
  9. data/{bin → lib/henshin/exec}/files.rb +0 -0
  10. data/lib/henshin/ext.rb +35 -58
  11. data/lib/henshin/gen.rb +83 -68
  12. data/lib/henshin/labels.rb +144 -0
  13. data/lib/henshin/plugin.rb +70 -33
  14. data/lib/henshin/plugins/highlight.rb +19 -26
  15. data/lib/henshin/plugins/liquid.rb +50 -52
  16. data/lib/henshin/plugins/maruku.rb +14 -16
  17. data/lib/henshin/plugins/sass.rb +19 -23
  18. data/lib/henshin/plugins/textile.rb +14 -16
  19. data/lib/henshin/post.rb +67 -120
  20. data/lib/henshin/site.rb +154 -131
  21. data/lib/henshin/static.rb +10 -8
  22. data/test/helper.rb +20 -8
  23. data/test/site/css/{includes/reset.sass → _reset.sass} +0 -0
  24. data/test/site/css/screen.sass +1 -1
  25. data/test/site/index.html +2 -3
  26. data/test/site/layouts/archive_date.html +5 -4
  27. data/test/site/layouts/archive_month.html +10 -5
  28. data/test/site/layouts/archive_year.html +13 -6
  29. data/test/site/layouts/category_page.html +7 -7
  30. data/test/site/layouts/main.html +3 -4
  31. data/test/site/layouts/post.html +1 -1
  32. data/test/site/layouts/tag_index.html +3 -2
  33. data/test/site/layouts/tag_page.html +6 -6
  34. data/test/site/options.yaml +2 -2
  35. data/test/site/plugins/test.rb +1 -3
  36. data/test/site/posts/Testing-Stuff.markdown +1 -1
  37. data/test/site/posts/Textile-Test.textile +1 -1
  38. data/test/site/posts/lorem-ipsum.markdown +1 -1
  39. data/test/site/posts/same-date.markdown +1 -1
  40. data/test/{test_henshin.rb → suite.rb} +0 -1
  41. data/test/test_gen.rb +98 -0
  42. data/test/test_options.rb +34 -19
  43. data/test/test_post.rb +67 -0
  44. data/test/test_site.rb +159 -46
  45. data/test/test_static.rb +13 -0
  46. metadata +53 -32
  47. data/lib/henshin/categories.rb +0 -29
  48. data/lib/henshin/tags.rb +0 -28
  49. data/test/test_archives.rb +0 -27
  50. data/test/test_categories.rb +0 -0
  51. data/test/test_gens.rb +0 -54
  52. data/test/test_layouts.rb +0 -14
  53. data/test/test_posts.rb +0 -75
  54. data/test/test_statics.rb +0 -0
  55. data/test/test_tags.rb +0 -0
  56. data/test/text_exts.rb +0 -0
@@ -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
- def initialize( config )
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
- @config = config
41
+ self.configure(override)
42
+ self.load_plugins
43
+ self
11
44
  end
12
45
 
13
- # Resets everything
14
- def reset
15
- @posts = []
16
- @gens = []
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 = Archive.new( self )
20
- @tags = Hash.new { |h, k| h[k] = Tag.new(k) }
21
- @categories = Hash.new { |h, k| h[k] = Category.new(k) }
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
- @layouts = {}
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 everything
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(@config[:root], 'layouts')
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(@config[:root], 'posts')
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(@config[:root], '**', '*.*') )
158
+ files = Dir.glob( File.join(@root, '**', '*.*') )
67
159
  gens = files.select {|i| gen?(i) }
68
- gens.each do |g|
69
- @gens << Gen.new(g, self)
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(@config[:root], '**', '*.*') )
167
+ files = Dir.glob( File.join(@root, '**', '*.*') )
76
168
  static = files.select {|i| static?(i) }
77
- static.each do |s|
78
- @statics << Static.new(s, self)
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.each_parallel {|g| g.process}
179
+ @gens.sort!
89
180
 
90
- self.build_tags
91
- self.build_categories
92
- self.build_archive
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
- 'site' => {
99
- 'author' => @config[:author],
100
- 'title' => @config[:title],
101
- 'description' => @config[:description],
102
- 'time_zone' => @config[:time_zone],
103
- 'created_at' => Time.now,
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.each_parallel {|p| p.render}
137
- @gens.each {|g| g.render}
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.each_parallel {|p| p.write}
145
- @gens.each_parallel {|g| g.write}
146
- @statics.each_parallel {|s| s.write}
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
- self.write_tags
150
- self.write_categories
151
- end
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
- # @return [Bool]
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
- # @return [Bool]
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
- # @return [Bool]
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
- # @return [Bool]
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 @config[:plugins][:generators].has_key? path.extension
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
- # @return [Bool]
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[:exclude]
232
- ignored.collect! {|i| File.join(@config[:root], i)}
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
@@ -2,25 +2,27 @@ module Henshin
2
2
 
3
3
  class Static
4
4
 
5
- attr_accessor :path, :site, :config, :content
5
+ attr_accessor :path, :site, :content
6
6
 
7
7
  def initialize( path, site )
8
8
  @path = path
9
9
  @site = site
10
- @config = site.config
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
- write_path = File.join( config[:root], config[:target], @path[ config[:root].size..-1 ] )
18
-
19
- FileUtils.mkdir_p File.join( write_path.directory )
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}>"
@@ -1,22 +1,23 @@
1
- require 'rubygems'
2
1
  require 'test/unit'
3
2
  require 'shoulda'
4
3
  require 'rr'
5
4
 
6
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
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
- override = {:root => File.join(File.dirname(__FILE__), 'site'), :target => '_site'}
28
- config = Henshin.configure(override)
29
- Henshin::Site.new(config)
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