henshin 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.2.2
data/bin/henshin CHANGED
@@ -2,98 +2,174 @@
2
2
 
3
3
  require File.dirname(__FILE__) + '/../lib/henshin'
4
4
  require 'optparse'
5
+ require 'fileutils'
5
6
 
6
7
  banner = <<EOS
7
- Usage: henshin
8
- henshin [path to source]
9
- henshin [path to source]:[path to write]
8
+ Usage: henshin
9
+ henshin [source]
10
+ henshin [source]:[destination]
11
+
12
+ henshin create
13
+ henshin create [path for site]
14
+
10
15
  EOS
11
16
 
17
+ opt_content = <<EOS
18
+ title: Test Site
19
+ author: John Doe
20
+ layout: main
21
+ EOS
12
22
 
13
- override = {}
14
- build = {:server => false, :auto => false}
15
- opts = OptionParser.new do |opts|
16
- opts.banner = banner
17
-
18
- opts.on("--server", "-s", "Start server") do
19
- build[:server] = true
20
- end
21
-
22
- opts.on("--auto", "-a", "Auto regenerate files") do
23
- build[:auto] = true
24
- end
25
-
26
- opts.on("--version", "Display current version of Henshin") do
27
- puts "Henshin #{Henshin.version}"
28
- exit 0
29
- end
23
+ index_content = <<EOS
24
+ ---
25
+ title: The Home Page
26
+ ---
27
+
28
+ Well you might want to change this!
29
+ EOS
30
+
31
+ layout_content = <<EOS
32
+ <!DOCTYPE html>
33
+ <html lang="en">
34
+ <head>
35
+ <meta charset="utf-8" />
36
+ <title>{{ site.title }}</title>
37
+ </head>
38
+ <body>
30
39
 
31
- end
32
- opts.parse!
40
+ {{ yield }}
41
+
42
+ <h4>A List of Posts</h4>
43
+ <ul>
44
+ {% for post in site.posts %}
45
+ <li><a href="{{ post.url }}">{{ post.title }}</a> - {{ post.date }}</li>
46
+ {% endfor %}
47
+ </ul>
48
+
49
+ <span>Copyright &copy; {{ site.author }}</span>
50
+ </body>
51
+ </html>
52
+ EOS
33
53
 
34
- if ARGV[0]
35
- override[:root] = ARGV[0].split(':')[0]
36
- override[:target] = ARGV[0].split(':')[1] if ARGV[0].split(':')[1]
37
- end
54
+ post_content = <<EOS
55
+ ---
56
+ title: Hello World
57
+ date: #{Time.now.strftime("%Y-%m-%d at %H:%M:%S")}
58
+ tags: test, hello
59
+ ---
38
60
 
61
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
62
+ EOS
39
63
 
40
- config = Henshin.configure(override)
41
- site = Henshin::Site.new(config)
42
64
 
65
+ if ARGV[0] == "create"
66
+ if ARGV[1]
67
+ # create the directory
68
+ FileUtils.mkdir_p ARGV[1]
69
+ # and cd to it
70
+ Dir.chdir ARGV[1]
71
+ end
72
+
73
+ # create directories
74
+ dirs = ['layouts', 'posts']
75
+ dirs.each {|d| FileUtils.mkdir_p d}
43
76
 
44
- # build normally
45
- puts "Building site..."
46
- site.build
47
- puts "Site created in #{config[:target]}"
77
+ # write files
78
+ files = {'options.yaml' => opt_content,
79
+ 'index.html' => index_content,
80
+ 'layouts/main.html' => layout_content,
81
+ 'posts/hello-world.markdown' => post_content}
82
+ files.each do |f, c|
83
+ file = File.new( f, "w" )
84
+ file.puts c
85
+ end
48
86
 
87
+ else
49
88
 
50
- # regenerate files when changed
51
- if build[:auto]
52
- require 'directory_watcher'
53
-
54
- puts "", "Auto-build initiated..."
55
-
56
- # build the glob pattern
57
- gl = Dir[ File.join(config[:root], '*')].select { |x| File.directory?(x) }
58
- ['/_site', '/plugins'].each do |r|
59
- gl = gl.select {|i| !i.include?( File.join(config[:root], r) )}
89
+ override = {}
90
+ build = {:server => false, :auto => false}
91
+ opts = OptionParser.new do |opts|
92
+ opts.banner = banner
93
+
94
+ opts.on("--server", "-s", "Start server") do
95
+ build[:server] = true
96
+ end
97
+
98
+ opts.on("--auto", "-a", "Auto regenerate files") do
99
+ build[:auto] = true
100
+ end
101
+
102
+ opts.on("--version", "Display current version of Henshin") do
103
+ puts "Henshin #{Henshin.version}"
104
+ exit 0
105
+ end
106
+
60
107
  end
61
- gl.collect! {|x| "#{x}/**/*"}
62
- gl += ['*']
108
+ opts.parse!
63
109
 
64
- dw = DirectoryWatcher.new config[:root], :glob => gl
65
- dw.interval = 2.0
66
- dw.add_observer do |*args|
67
- if args.size > 1
68
- puts "rebuilding -> #{args.size} files"
69
- else
70
- puts "rebuilding -> #{args[0].path}"
71
- end
72
- site.build
110
+ if ARGV[0]
111
+ override[:root] = ARGV[0].split(':')[0]
112
+ override[:target] = ARGV[0].split(':')[1] if ARGV[0].split(':')[1]
73
113
  end
74
114
 
75
- dw.start
76
115
 
77
- unless build[:server]
78
- loop {sleep 1000}
79
- end
80
- end
81
-
82
-
83
- # start a server
84
- if build[:server]
85
- require 'webrick'
116
+ config = Henshin.configure(override)
117
+ site = Henshin::Site.new(config)
86
118
 
87
- server = WEBrick::HTTPServer.new(
88
- :Port => '3000',
89
- :DocumentRoot => File.join(config[:root], config[:target]),
90
- :MimeTypes => WEBrick::HTTPUtils::DefaultMimeTypes
91
- )
119
+ start = Time.now
120
+ # build normally
121
+ puts "Building site..."
122
+ site.build
123
+ puts "Site created in #{config[:target]} (#{Time.now - start}s)"
92
124
 
93
- thread = Thread.new { server.start }
94
- trap("INT") { server.shutdown }
95
125
 
96
- thread.join()
97
- end
98
-
126
+ # regenerate files when changed
127
+ if build[:auto]
128
+ require 'directory_watcher'
129
+
130
+ puts "", "Auto-build initiated..."
131
+
132
+ # build the glob pattern
133
+ gl = Dir[ File.join(config[:root], '*')].select { |x| File.directory?(x) }
134
+ ['/_site', '/plugins'].each do |r|
135
+ gl = gl.select {|i| !i.include?( File.join(config[:root], r) )}
136
+ end
137
+ gl.collect! {|x| "#{x}/**/*"}
138
+ gl += ['*']
139
+
140
+ dw = DirectoryWatcher.new config[:root], :glob => gl
141
+ dw.interval = 2.0
142
+ dw.add_observer do |*args|
143
+ if args.size > 1
144
+ puts "rebuilding -> #{args.size} files"
145
+ else
146
+ puts "rebuilding -> #{args[0].path}"
147
+ end
148
+ site.build
149
+ end
150
+
151
+ dw.start
152
+
153
+ unless build[:server]
154
+ loop {sleep 1000}
155
+ end
156
+ end
157
+
158
+
159
+ # start a server
160
+ if build[:server]
161
+ require 'webrick'
162
+
163
+ server = WEBrick::HTTPServer.new(
164
+ :Port => '3000',
165
+ :DocumentRoot => File.join(config[:root], config[:target]),
166
+ :MimeTypes => WEBrick::HTTPUtils::DefaultMimeTypes
167
+ )
168
+
169
+ thread = Thread.new { server.start }
170
+ trap("INT") { server.shutdown }
171
+
172
+ thread.join()
173
+ end
99
174
 
175
+ end
data/henshin.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{henshin}
8
- s.version = "0.2.1"
8
+ s.version = "0.2.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["hawx"]
12
- s.date = %q{2010-05-31}
12
+ s.date = %q{2010-06-06}
13
13
  s.default_executable = %q{henshin}
14
14
  s.description = %q{Henshin is a static site generator, with a plugin system and more}
15
15
  s.email = %q{m@hawx.me}
@@ -35,6 +35,7 @@ Gem::Specification.new do |s|
35
35
  "lib/henshin/plugins/maruku.rb",
36
36
  "lib/henshin/plugins/pygments.rb",
37
37
  "lib/henshin/plugins/sass.rb",
38
+ "lib/henshin/plugins/textile.rb",
38
39
  "lib/henshin/post.rb",
39
40
  "lib/henshin/site.rb",
40
41
  "lib/henshin/static.rb",
@@ -45,6 +46,7 @@ Gem::Specification.new do |s|
45
46
  "test/site/_site/css/print.css",
46
47
  "test/site/_site/index.html",
47
48
  "test/site/_site/static.html",
49
+ "test/site/css/print.sass",
48
50
  "test/site/css/screen.css",
49
51
  "test/site/index.html",
50
52
  "test/site/layouts/category_index.html",
@@ -56,10 +58,12 @@ Gem::Specification.new do |s|
56
58
  "test/site/options.yaml",
57
59
  "test/site/plugins/test.rb",
58
60
  "test/site/posts/Testing-Stuff.markdown",
61
+ "test/site/posts/Textile-Test.textile",
59
62
  "test/site/posts/cat/test.markdown",
60
63
  "test/site/posts/lorem-ipsum.markdown",
61
- "test/site/sass/print.sass",
64
+ "test/site/posts/same-date.markdown",
62
65
  "test/site/static.html",
66
+ "test/test_gens.rb",
63
67
  "test/test_henshin.rb",
64
68
  "test/test_layouts.rb",
65
69
  "test/test_options.rb",
@@ -74,6 +78,7 @@ Gem::Specification.new do |s|
74
78
  s.test_files = [
75
79
  "test/helper.rb",
76
80
  "test/site/plugins/test.rb",
81
+ "test/test_gens.rb",
77
82
  "test/test_henshin.rb",
78
83
  "test/test_layouts.rb",
79
84
  "test/test_options.rb",
data/lib/henshin/ext.rb CHANGED
@@ -84,4 +84,46 @@ class String
84
84
  self.dup.gsub(/([a-zA-Z0-9_-]+\/)/, '')
85
85
  end
86
86
 
87
+
88
+
89
+ # Methods to determine whether the file at the path is a post, layout, gen or static
90
+
91
+ # @param [Hash] config
92
+ # @return [Bool]
93
+ # @todo see #ignored?
94
+ def static?( config )
95
+ !( self.layout? || self.post? || self.gen?(config) || self.ignored?(config) )
96
+ end
97
+
98
+ # @return [Bool]
99
+ def layout?
100
+ self.include? 'layouts/'
101
+ end
102
+
103
+ # @return [Bool]
104
+ def post?
105
+ self.include? 'posts/'
106
+ end
107
+
108
+ # @param [Hash] config
109
+ # @return [Bool]
110
+ # @todo see #ignored?
111
+ def gen?( config )
112
+ return true if config[:plugins][:generators].has_key? self.extension
113
+ return true if File.open(self, "r").read(3) == "---"
114
+ false
115
+ end
116
+
117
+ # @param [Hash] config
118
+ # @return [Bool]
119
+ # @todo Find a way around having to pass the config in
120
+ def ignored?( config )
121
+ ignored = ['/options.yaml'] + config[:exclude]
122
+ ignored.collect! {|i| File.join(config[:root], i)}
123
+ ignored.each do |i|
124
+ return true if self.include? i
125
+ end
126
+ false
127
+ end
128
+
87
129
  end
data/lib/henshin/gen.rb CHANGED
@@ -4,9 +4,9 @@ module Henshin
4
4
  class Gen
5
5
 
6
6
  attr_accessor :path, :extension, :content, :layout, :date, :title
7
- attr_accessor :site, :config, :renderer, :data
7
+ attr_accessor :site, :config, :renderer, :data, :output
8
8
 
9
- def initialize( path, site, data={} )
9
+ def initialize( path, site, data=nil )
10
10
  @path = path
11
11
  @site = site
12
12
  @config = site.config
@@ -38,78 +38,98 @@ module Henshin
38
38
  #
39
39
  # @param [Hash] override data to override settings with
40
40
  def override( override )
41
- @layout ||= @site.layouts[ override[:layout] ]
42
- @date ||= Time.parse( override[:date].to_s )
41
+ @title = override[:title] if override[:title]
42
+ @layout = @site.layouts[ override[:layout] ] if override[:layout]
43
+ @date = Time.parse( override[:date].to_s ) if override[:date]
43
44
  end
44
45
 
45
46
 
46
47
  ##
47
48
  # Renders the files content
48
49
  def render
49
- # render the posts content
50
- config[:plugins].each do |plugin|
51
- if plugin.extensions[:input].include?( @extension ) && plugin.is_a?( Generator )
52
- @content = plugin.generate( @content )
53
- @renderer = plugin
54
- end
55
- end
56
- @renderer ||= StandardPlugin.new
50
+ ignore_layout = false
57
51
 
52
+ if config[:plugins][:generators].has_key? @extension
53
+ plugin = config[:plugins][:generators][@extension]
54
+ @content = plugin.generate( @content )
55
+ @output = plugin.extensions[:output]
56
+ ignore_layout = true if plugin.config[:ignore_layouts]
57
+ end
58
+
58
59
  @layout ||= site.layouts[ site.config[:layout] ]
59
- unless @renderer.config[:ignore_layouts]
60
- # do the layout
61
- config[:plugins].each do |plugin|
62
- if plugin.is_a?( LayoutParser )
63
- @content = plugin.generate( @layout, self.payload )
64
- end
60
+ unless ignore_layout || @layout.nil?
61
+ config[:plugins][:layout_parsers].each do |plugin|
62
+ @content = plugin.generate( @layout, self.payload )
65
63
  end
66
64
  end
65
+
67
66
  end
68
67
 
69
- # Creates the data to be sent to the layout engine. Uses optional data if available
68
+ # Creates the data to be sent to the layout engine. Adds optional data if available
70
69
  #
71
70
  # @return [Hash] the payload for the layout engine
72
71
  def payload
73
- if @data == {}
74
- {
75
- 'yield' => @content,
76
- 'site' => @site.payload['site']
77
- }
78
- else
79
- {
80
- 'yield' => @content,
81
- 'site' => @site.payload['site'],
82
- @data[:name] => @data[:payload]
83
- }
84
- end
72
+ hash = {
73
+ 'yield' => @content,
74
+ 'gen' => self.to_hash,
75
+ 'site' => @site.payload['site'],
76
+ }
77
+ hash[ @data[:name] ] = @data[:payload] if @data
78
+
79
+ hash
80
+ end
81
+
82
+ # Turns all of the post data into a hash
83
+ #
84
+ # @return [Hash]
85
+ def to_hash
86
+ {
87
+ 'title' => @title,
88
+ 'permalink' => self.permalink,
89
+ 'url' => self.url,
90
+ 'date' => @date,
91
+ 'content' => @content
92
+ }
85
93
  end
86
94
 
87
95
 
88
96
  ##
89
97
  # Writes the file to the correct place
90
98
  def write
91
-
92
99
  write_path = File.join( config[:root], config[:target], @path[config[:root].size..-1] )
93
-
94
- render_target = @renderer.config[:target] if @renderer
95
- if render_target
96
- # files should be put in a different folder
97
- write_path.gsub!("/#{@renderer.config[:root]}", "/#{@renderer.config[:target]}")
98
- end
99
100
 
100
- render_type = @renderer.extensions[:output] if @renderer
101
- if render_type != ''
102
- # files should have different extension
103
- write_path.gsub!(".#{@extension}", ".#{render_type}")
104
- end
101
+ # change extension if necessary
102
+ write_path.gsub!(".#{@extension}", ".#{@output}") if @output
105
103
 
106
104
  FileUtils.mkdir_p File.join( write_path.directory )
107
105
  file = File.new( File.join( write_path ), "w" )
108
106
  file.puts( @content )
109
-
107
+ end
108
+
109
+ # Returns the permalink for the gen
110
+ def permalink
111
+ @path[config[:root].size..-1]
112
+ end
113
+
114
+ # Returns the (pretty) url for the gen
115
+ def url
116
+ if config[:permalink].include? "/index.html"
117
+ self.permalink[0..-11]
118
+ else
119
+ self.permalink
120
+ end
110
121
  end
111
122
 
112
123
 
124
+ # Needed to sort the posts by date, newest first
125
+ def <=>( val )
126
+ s = self.date <=> val.date
127
+ if s == 0
128
+ return self.permalink <=> val.permalink
129
+ else
130
+ return -1 * s
131
+ end
132
+ end
113
133
 
114
134
  def inspect
115
135
  "#<Gen:#{@path}>"
@@ -16,6 +16,10 @@ module Henshin
16
16
  @config = {}
17
17
  end
18
18
 
19
+ # def configure( override )
20
+ # setup the plugin
21
+ # end
22
+
19
23
  # Uncomment to have the plugin loaded
20
24
  # Henshin.register! self
21
25
  end
@@ -1,7 +1,7 @@
1
1
  require 'henshin/plugin'
2
2
  require 'maruku'
3
3
 
4
- class MarukuPlug < Henshin::Generator
4
+ class MarukuPlugin < Henshin::Generator
5
5
 
6
6
  attr_accessor :extensions, :config
7
7
 
@@ -1,20 +1,21 @@
1
1
  require 'henshin/plugin'
2
- require 'haml'
3
- require 'sass/engine'
2
+ require 'sass'
4
3
 
5
4
  class SassPlugin < Henshin::Generator
6
5
 
7
- attr_accessor :extensions, :config
6
+ attr_accessor :extensions, :config, :opts_name
8
7
 
9
- Defaults = {:target => 'css',
10
- :root => 'sass',
11
- :ignore_layouts => true,
8
+ Defaults = {:ignore_layouts => true,
12
9
  :style => :nested}
13
10
 
14
- def initialize( override={} )
11
+ def initialize
15
12
  @extensions = {:input => ['sass', 'scss'],
16
13
  :output => 'css'}
17
- @config = Defaults.merge(override)
14
+ @opts_name = :sass
15
+ end
16
+
17
+ def configure( override )
18
+ override ? @config = Defaults.merge(override) : @config = Defaults
18
19
  end
19
20
 
20
21
  def generate( content )
@@ -0,0 +1,21 @@
1
+ require 'henshin/plugin'
2
+ require 'redcloth'
3
+
4
+ class TextilePlugin < Henshin::Generator
5
+
6
+ attr_accessor :extensions, :config
7
+
8
+ Defaults = {}
9
+
10
+ def initialize( override={} )
11
+ @extensions = {:input => ['textile'],
12
+ :output => 'html'}
13
+ @config = Defaults.merge(override)
14
+ end
15
+
16
+ def generate( content )
17
+ RedCloth.new(content).to_html
18
+ end
19
+
20
+ Henshin.register! self
21
+ end
data/lib/henshin/post.rb CHANGED
@@ -25,17 +25,18 @@ module Henshin
25
25
 
26
26
  # Reads the filename and extracts information from it
27
27
  def read_name
28
+
28
29
  parser = {'title' => '([a-zA-Z0-9 ]+)',
29
30
  'title-with-dashes' => '([a-zA-Z0-9-]+)',
30
31
  'date' => '(\d{4}-\d{2}-\d{2})',
31
32
  'date-time' => '(\d{4}-\d{2}-\d{2} at \d{2}:\d{2})',
32
33
  'xml-date-time' => '(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?((\+|-)\d{2}:\d{2})?)',
33
34
  'category' => '([a-zA-Z0-9_ -]+)',
34
- 'extension' => "(#{ site.config[:extensions].join('|') })"}
35
+ 'extension' => "([a-zA-Z0-9_-]+)"}
35
36
 
36
37
  file_parser = config[:file_name]
37
38
  data_order = []
38
-
39
+
39
40
  # put together regex
40
41
  m = file_parser.gsub(/\{([a-z-]+)\}/) do
41
42
  data_order << $1
@@ -53,7 +54,7 @@ module Henshin
53
54
  matcher = Regexp.new(m)
54
55
 
55
56
  override = {}
56
- name = path[ (config[:root]+'/posts/').size..-1 ]
57
+ name = @path[ (config[:root]+'/posts/').size..-1 ]
57
58
 
58
59
  file_data = name.match( matcher ).captures
59
60
  file_data.each_with_index do |data, i|
@@ -113,8 +114,8 @@ module Henshin
113
114
  def payload
114
115
  {
115
116
  'yield' => @content,
116
- 'site' => @site.payload['site'],
117
- 'post' => self.to_hash
117
+ 'site' => @site.payload['site'],
118
+ 'post' => self.to_hash
118
119
  }
119
120
  end
120
121
 
@@ -125,7 +126,8 @@ module Henshin
125
126
  {
126
127
  'title' => @title,
127
128
  'author' => @author,
128
- 'url' => self.permalink,
129
+ 'permalink' => self.permalink,
130
+ 'url' => self.url,
129
131
  'date' => @date,
130
132
  'category' => @category,
131
133
  'tags' => @tags,