nanoc 1.4 → 1.5
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/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
|