nanoc 1.4 → 1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +7 -0
- data/README +2 -24
- data/Rakefile +1 -1
- data/bin/nanoc +4 -4
- data/lib/nanoc.rb +13 -5
- data/lib/{compiler.rb → nanoc/compiler.rb} +80 -53
- data/lib/nanoc/core_ext.rb +1 -0
- data/lib/nanoc/core_ext/array.rb +17 -0
- data/lib/nanoc/core_ext/hash.rb +41 -0
- data/lib/nanoc/core_ext/string.rb +13 -0
- data/lib/nanoc/core_ext/yaml.rb +8 -0
- data/lib/{creator.rb → nanoc/creator.rb} +7 -7
- data/lib/nanoc/enhancements.rb +93 -0
- data/lib/nanoc/filters.rb +7 -0
- data/lib/nanoc/filters/eruby_filter.rb +44 -0
- data/lib/nanoc/filters/haml_filter.rb +24 -0
- data/lib/nanoc/filters/liquid_filter.rb +51 -0
- data/lib/nanoc/filters/markaby_filter.rb +25 -0
- data/lib/nanoc/filters/markdown_filter.rb +21 -0
- data/lib/nanoc/filters/rdoc_filter.rb +19 -0
- data/lib/nanoc/filters/sass_filter.rb +18 -0
- data/lib/nanoc/filters/smartypants_filter.rb +21 -0
- data/lib/nanoc/filters/textile_filter.rb +21 -0
- data/lib/nanoc/page.rb +147 -0
- metadata +25 -7
- data/lib/enhancements.rb +0 -277
- data/lib/page.rb +0 -91
@@ -0,0 +1,93 @@
|
|
1
|
+
def try_require(s) ; begin ; require s ; rescue LoadError ; end ; end
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
def handle_exception(exception, text)
|
6
|
+
unless $quiet or exception.class == SystemExit
|
7
|
+
$stderr.puts "ERROR: Exception occured while #{text}:\n"
|
8
|
+
$stderr.puts exception
|
9
|
+
$stderr.puts exception.backtrace.join("\n")
|
10
|
+
end
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
|
14
|
+
def deprecate(str)
|
15
|
+
$stderr.puts 'DEPRECATION WARNING: ' + str unless $quiet
|
16
|
+
end
|
17
|
+
|
18
|
+
class FileLogger
|
19
|
+
COLORS = {
|
20
|
+
:reset => "\e[0m",
|
21
|
+
|
22
|
+
:bold => "\e[1m",
|
23
|
+
|
24
|
+
:black => "\e[30m",
|
25
|
+
:red => "\e[31m",
|
26
|
+
:green => "\e[32m",
|
27
|
+
:yellow => "\e[33m",
|
28
|
+
:blue => "\e[34m",
|
29
|
+
:magenta => "\e[35m",
|
30
|
+
:cyan => "\e[36m",
|
31
|
+
:white => "\e[37m"
|
32
|
+
}
|
33
|
+
|
34
|
+
ACTION_COLORS = {
|
35
|
+
:create => COLORS[:bold] + COLORS[:green],
|
36
|
+
:update => COLORS[:bold] + COLORS[:yellow],
|
37
|
+
:move => COLORS[:bold] + COLORS[:blue],
|
38
|
+
:identical => COLORS[:bold]
|
39
|
+
}
|
40
|
+
|
41
|
+
attr_reader :out
|
42
|
+
|
43
|
+
def initialize(a_out = $stdout)
|
44
|
+
@out = a_out
|
45
|
+
end
|
46
|
+
|
47
|
+
def log(a_action, a_path)
|
48
|
+
@out.puts('%s%12s%s %s' % [ACTION_COLORS[a_action.to_sym], a_action, COLORS[:reset], a_path]) unless $quiet
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def method_missing(a_method, *a_args)
|
54
|
+
log(a_method.to_s, a_args.first)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class FileManager
|
59
|
+
@@stack = []
|
60
|
+
@@logger = FileLogger.new
|
61
|
+
|
62
|
+
def self.create_dir(a_name)
|
63
|
+
@@stack.pushing(a_name) do
|
64
|
+
path = File.join(@@stack)
|
65
|
+
unless File.directory?(path)
|
66
|
+
FileUtils.mkdir_p(path)
|
67
|
+
@@logger.create(path)
|
68
|
+
end
|
69
|
+
yield if block_given?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.create_file(a_name)
|
74
|
+
path = File.join(@@stack + [ a_name ])
|
75
|
+
FileManager.create_dir(path.sub(/\/[^\/]+$/, '')) if @@stack.empty?
|
76
|
+
content = block_given? ? yield : nil
|
77
|
+
if File.exist?(path)
|
78
|
+
if block_given? and File.read(path) == content
|
79
|
+
@@logger.identical(path)
|
80
|
+
else
|
81
|
+
@@logger.update(path)
|
82
|
+
end
|
83
|
+
else
|
84
|
+
@@logger.create(path)
|
85
|
+
end
|
86
|
+
open(path, 'w') { |io| io.write(content) unless content.nil? }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def render(a_name, a_context={})
|
91
|
+
assigns = a_context.merge({ :page => @page, :pages => @pages })
|
92
|
+
File.read('layouts/' + a_name.to_s + '.erb').eruby(:assigns => assigns)
|
93
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# Convenience function for registering filters
|
2
|
+
def register_filter(*names, &block)
|
3
|
+
names.each { |name| $nanoc_compiler.register_filter(name, &block) }
|
4
|
+
end
|
5
|
+
|
6
|
+
# Load all filters
|
7
|
+
Dir[File.join(File.dirname(__FILE__), 'filters', '*_filter.rb')].each { |f| require f }
|
@@ -0,0 +1,44 @@
|
|
1
|
+
def try_require(s) ; begin ; require s ; rescue LoadError ; end ; end
|
2
|
+
|
3
|
+
try_require 'rubygems'
|
4
|
+
|
5
|
+
require 'erb'
|
6
|
+
try_require 'erubis'
|
7
|
+
|
8
|
+
class ERBContext
|
9
|
+
|
10
|
+
def initialize(hash)
|
11
|
+
hash.each_pair do |key, value|
|
12
|
+
instance_variable_set('@' + key.to_s, value)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_binding
|
17
|
+
binding
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class String
|
23
|
+
|
24
|
+
# Converts the string using eRuby
|
25
|
+
def eruby(params={})
|
26
|
+
params[:eruby_engine] == :erubis ? erubis(params) : erb(params)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Converts the string using Erubis
|
30
|
+
def erubis(params={})
|
31
|
+
Erubis::Eruby.new(self).evaluate(params[:assigns] || {})
|
32
|
+
end
|
33
|
+
|
34
|
+
# Converts the string using ERB
|
35
|
+
def erb(params={})
|
36
|
+
ERB.new(self).result(ERBContext.new(params[:assigns] || {}).get_binding)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
register_filter 'eruby' do |page, pages, config|
|
42
|
+
assigns = { :page => page, :pages => pages }
|
43
|
+
page.content.eruby(:assigns => assigns, :eruby_engine => config[:eruby_engine])
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
def try_require(s) ; begin ; require s ; rescue LoadError ; end ; end
|
2
|
+
|
3
|
+
try_require 'rubygems'
|
4
|
+
|
5
|
+
try_require 'haml'
|
6
|
+
|
7
|
+
class String
|
8
|
+
|
9
|
+
# Converts the string using Haml
|
10
|
+
def haml(params={})
|
11
|
+
options = (params[:haml_options] || {})
|
12
|
+
options[:locals] = params[:assigns] unless params[:assigns].nil?
|
13
|
+
Haml::Engine.new(self, options).to_html
|
14
|
+
rescue NameError
|
15
|
+
$stderr.puts 'ERROR: String#haml failed (Haml not installed?)' unless $quiet
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
register_filter 'haml' do |page, pages, config|
|
22
|
+
assigns = { :page => page, :pages => pages }
|
23
|
+
page.content.haml(:assigns => assigns, :haml_options => page[:haml_options])
|
24
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
def try_require(s) ; begin ; require s ; rescue LoadError ; end ; end
|
2
|
+
|
3
|
+
try_require 'rubygems'
|
4
|
+
|
5
|
+
try_require 'liquid'
|
6
|
+
|
7
|
+
class String
|
8
|
+
|
9
|
+
# Converts the string using Liquid
|
10
|
+
def liquid(params={})
|
11
|
+
Liquid::Template.parse(self).render((params[:assigns] || {}).stringify_keys)
|
12
|
+
rescue NameError
|
13
|
+
$stderr.puts 'ERROR: String#liquid failed (Liquid not installed?)' unless $quiet
|
14
|
+
exit
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
begin
|
20
|
+
class Nanoc::LiquidRenderTag < ::Liquid::Tag
|
21
|
+
Syntax = /(['"])([^'"]+)\1/
|
22
|
+
|
23
|
+
def initialize(markup, tokens)
|
24
|
+
if markup =~ Syntax
|
25
|
+
@layout_name = $2
|
26
|
+
else
|
27
|
+
raise SyntaxError.new("Error in tag 'render' - Valid syntax: render '[layout]'")
|
28
|
+
end
|
29
|
+
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse(tokens)
|
34
|
+
end
|
35
|
+
|
36
|
+
def render(context)
|
37
|
+
source = File.read('layouts/' + @layout_name + '.liquid')
|
38
|
+
partial = Liquid::Template.parse(source)
|
39
|
+
|
40
|
+
partial.render(context)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
Liquid::Template.register_tag('render', Nanoc::LiquidRenderTag)
|
45
|
+
rescue NameError
|
46
|
+
end
|
47
|
+
|
48
|
+
register_filter 'liquid' do |page, pages, config|
|
49
|
+
assigns = { :page => page, :pages => pages }
|
50
|
+
page.content.liquid(:assigns => assigns)
|
51
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
def try_require(s) ; begin ; require s ; rescue LoadError ; end ; end
|
2
|
+
|
3
|
+
try_require 'rubygems'
|
4
|
+
|
5
|
+
require 'erb'
|
6
|
+
|
7
|
+
try_require 'markaby'
|
8
|
+
|
9
|
+
class String
|
10
|
+
|
11
|
+
# Converts the string using Markaby
|
12
|
+
# TODO perhaps add support for helpers
|
13
|
+
def markaby(params={})
|
14
|
+
Markaby::Builder.new((params[:assigns] || {})).instance_eval(self).to_s
|
15
|
+
rescue NameError
|
16
|
+
$stderr.puts 'ERROR: String#markaby failed (Markaby not installed?)' unless $quiet
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
register_filter 'markaby' do |page, pages, config|
|
23
|
+
assigns = { :page => page, :pages => pages }
|
24
|
+
page.content.markaby(:assigns => assigns)
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
def try_require(s) ; begin ; require s ; rescue LoadError ; end ; end
|
2
|
+
|
3
|
+
try_require 'rubygems'
|
4
|
+
|
5
|
+
try_require 'bluecloth'
|
6
|
+
|
7
|
+
class String
|
8
|
+
|
9
|
+
# Converts the string to HTML using BlueCloth/Markdown.
|
10
|
+
def markdown
|
11
|
+
BlueCloth.new(self).to_html
|
12
|
+
rescue NameError
|
13
|
+
$stderr.puts 'ERROR: String#markdown failed: BlueCloth not installed' unless $quiet
|
14
|
+
exit
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
register_filter 'markdown', 'bluecloth' do |page, pages, config|
|
20
|
+
page.content.markdown
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
def try_require(s) ; begin ; require s ; rescue LoadError ; end ; end
|
2
|
+
|
3
|
+
try_require 'rubygems'
|
4
|
+
|
5
|
+
try_require 'rdoc/markup/simple_markup'
|
6
|
+
try_require 'rdoc/markup/simple_markup/to_html'
|
7
|
+
|
8
|
+
class String
|
9
|
+
|
10
|
+
# Converts the string using RDoc
|
11
|
+
def rdoc
|
12
|
+
SM::SimpleMarkup.new.convert(self, SM::ToHtml.new)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
register_filter 'rdoc' do |page, pages, config|
|
18
|
+
page.content.rdoc
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
def try_require(s) ; begin ; require s ; rescue LoadError ; end ; end
|
2
|
+
|
3
|
+
try_require 'rubygems'
|
4
|
+
|
5
|
+
try_require 'haml'
|
6
|
+
|
7
|
+
class String
|
8
|
+
|
9
|
+
# Converts the string using Sass
|
10
|
+
def sass
|
11
|
+
Sass::Engine.new(self).render
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
register_filter 'sass' do |page, pages, config|
|
17
|
+
page.content.sass
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
def try_require(s) ; begin ; require s ; rescue LoadError ; end ; end
|
2
|
+
|
3
|
+
try_require 'rubygems'
|
4
|
+
|
5
|
+
try_require 'rubypants'
|
6
|
+
|
7
|
+
class String
|
8
|
+
|
9
|
+
# Converts the string using RubyPants/SmartyPants
|
10
|
+
def smartypants
|
11
|
+
RubyPants.new(self).to_html
|
12
|
+
rescue NameError
|
13
|
+
$stderr.puts 'ERROR: String#smartypants failed (RubyPants not installed?)' unless $quiet
|
14
|
+
exit
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
register_filter 'smartypants', 'rubypants' do |page, pages, config|
|
20
|
+
page.content.smartypants
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
def try_require(s) ; begin ; require s ; rescue LoadError ; end ; end
|
2
|
+
|
3
|
+
try_require 'rubygems'
|
4
|
+
|
5
|
+
try_require 'redcloth'
|
6
|
+
|
7
|
+
class String
|
8
|
+
|
9
|
+
# Converts the string using RedCloth/Textile
|
10
|
+
def textile
|
11
|
+
RedCloth.new(self).to_html
|
12
|
+
rescue NameError
|
13
|
+
$stderr.puts 'ERROR: String#textile failed (RedCloth not installed?)' unless $quiet
|
14
|
+
exit
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
register_filter 'textile', 'redcloth' do |page, pages, config|
|
20
|
+
page.content.textile
|
21
|
+
end
|
data/lib/nanoc/page.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
def try_require(s) ; begin ; require s ; rescue LoadError ; end ; end
|
2
|
+
|
3
|
+
try_require 'rubygems'
|
4
|
+
try_require 'liquid'
|
5
|
+
|
6
|
+
module Nanoc
|
7
|
+
|
8
|
+
# Page drop
|
9
|
+
|
10
|
+
begin
|
11
|
+
class PageDrop < ::Liquid::Drop
|
12
|
+
def initialize(page)
|
13
|
+
@page = page
|
14
|
+
end
|
15
|
+
|
16
|
+
def before_method(name)
|
17
|
+
name == 'content' ? @page.content : @page.attributes[name.to_sym]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
rescue NameError
|
21
|
+
class PageDrop
|
22
|
+
def initialize(*args)
|
23
|
+
$stderr.puts 'ERROR: Liquid not installed; cannot use Liquid in layouts.'
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Page proxy
|
30
|
+
|
31
|
+
class PageProxy
|
32
|
+
def initialize(page, params={})
|
33
|
+
@page = page
|
34
|
+
@do_compile = (params[:compile] != false)
|
35
|
+
end
|
36
|
+
|
37
|
+
def [](key)
|
38
|
+
if key.to_sym == :content and @do_compile
|
39
|
+
@page.content
|
40
|
+
else
|
41
|
+
if key.to_s.starts_with?('_')
|
42
|
+
nil
|
43
|
+
elsif key.to_s.ends_with?('?')
|
44
|
+
@page.attributes[key.to_s[0..-2].to_sym]
|
45
|
+
else
|
46
|
+
@page.attributes[key]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def method_missing(method, *args)
|
52
|
+
self[method]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Page
|
57
|
+
|
58
|
+
class Page
|
59
|
+
|
60
|
+
def initialize(hash={})
|
61
|
+
@attributes = hash
|
62
|
+
end
|
63
|
+
|
64
|
+
def attributes
|
65
|
+
@attributes
|
66
|
+
end
|
67
|
+
|
68
|
+
def content
|
69
|
+
compile
|
70
|
+
@attributes[:content]
|
71
|
+
end
|
72
|
+
|
73
|
+
# Proxy/Liquid support
|
74
|
+
|
75
|
+
def to_proxy(params={})
|
76
|
+
PageProxy.new(self, :compile => params[:compile])
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_liquid
|
80
|
+
PageDrop.new(self)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Compiling
|
84
|
+
|
85
|
+
def self.compile(pages)
|
86
|
+
@@compilation_stack = []
|
87
|
+
@@pages = pages
|
88
|
+
|
89
|
+
# Compile all pages
|
90
|
+
pages.each { |page| page.compile }
|
91
|
+
end
|
92
|
+
|
93
|
+
def compile
|
94
|
+
# Check for recursive call
|
95
|
+
if @@compilation_stack.include?(self)
|
96
|
+
# Print compilation stack
|
97
|
+
unless $quiet
|
98
|
+
$stderr.puts 'ERROR: Recursive call to page content.'
|
99
|
+
print_compilation_stack
|
100
|
+
end
|
101
|
+
|
102
|
+
exit
|
103
|
+
# Compile if not yet compiled
|
104
|
+
elsif @attributes[:content].nil?
|
105
|
+
@@compilation_stack.pushing(self) do
|
106
|
+
# Read page
|
107
|
+
content = File.read(@attributes[:_content_filename])
|
108
|
+
|
109
|
+
begin
|
110
|
+
# Get params
|
111
|
+
page = self.to_proxy(:compile => false)
|
112
|
+
pages = @@pages.map { |p| p.to_proxy }
|
113
|
+
config = $nanoc_compiler.config
|
114
|
+
|
115
|
+
# Filter page
|
116
|
+
@attributes[:content] = content
|
117
|
+
@attributes[:filters].each do |filter_name|
|
118
|
+
filter = $nanoc_compiler.filter_named(filter_name)
|
119
|
+
if filter.nil?
|
120
|
+
$stderr.puts 'WARNING: Unknown filter: ' + filter_name unless $quiet
|
121
|
+
else
|
122
|
+
@attributes[:content] = filter.call(page, pages, config)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
rescue Exception => exception
|
126
|
+
handle_exception(exception, "compiling page '#{@attributes[:_content_filename]}'")
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def print_compilation_stack
|
133
|
+
# Determine relevant part of compilation stack
|
134
|
+
stack_begin = @@compilation_stack.index(self)
|
135
|
+
stack_end = @@compilation_stack.size
|
136
|
+
relevant_stack_part = @@compilation_stack.last(stack_end - stack_begin)
|
137
|
+
|
138
|
+
# Print relevant part of compilation stack
|
139
|
+
$stderr.puts 'Page compilation stack:'
|
140
|
+
relevant_stack_part.each_with_index do |page, i|
|
141
|
+
$stderr.puts "#{i} #{page.attributes[:_content_filename]}"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|