nanoc 1.6.2 → 2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +27 -0
- data/Rakefile +34 -27
- data/bin/nanoc +153 -49
- data/lib/nanoc.rb +15 -33
- data/lib/nanoc/base/auto_compiler.rb +124 -0
- data/lib/nanoc/base/compiler.rb +55 -0
- data/lib/nanoc/base/core_ext/hash.rb +34 -0
- data/lib/nanoc/base/data_source.rb +53 -0
- data/lib/nanoc/base/enhancements.rb +89 -0
- data/lib/nanoc/base/filter.rb +16 -0
- data/lib/nanoc/base/layout_processor.rb +33 -0
- data/lib/nanoc/base/page.rb +155 -0
- data/lib/nanoc/base/page_proxy.rb +31 -0
- data/lib/nanoc/base/plugin.rb +19 -0
- data/lib/nanoc/base/plugin_manager.rb +33 -0
- data/lib/nanoc/base/site.rb +143 -0
- data/lib/nanoc/data_sources/database.rb +259 -0
- data/lib/nanoc/data_sources/filesystem.rb +308 -0
- data/lib/nanoc/data_sources/trivial.rb +145 -0
- data/lib/nanoc/filters/erb.rb +34 -0
- data/lib/nanoc/filters/haml.rb +16 -0
- data/lib/nanoc/filters/markaby.rb +15 -0
- data/lib/nanoc/filters/markdown.rb +13 -0
- data/lib/nanoc/filters/rdoc.rb +14 -0
- data/lib/nanoc/filters/smartypants.rb +13 -0
- data/lib/nanoc/filters/textile.rb +13 -0
- data/lib/nanoc/layout_processors/erb.rb +35 -0
- data/lib/nanoc/layout_processors/haml.rb +18 -0
- data/lib/nanoc/layout_processors/markaby.rb +16 -0
- metadata +37 -30
- data/lib/nanoc/compiler.rb +0 -145
- data/lib/nanoc/core_ext.rb +0 -1
- data/lib/nanoc/core_ext/array.rb +0 -17
- data/lib/nanoc/core_ext/hash.rb +0 -43
- data/lib/nanoc/core_ext/string.rb +0 -13
- data/lib/nanoc/core_ext/yaml.rb +0 -10
- data/lib/nanoc/creator.rb +0 -180
- data/lib/nanoc/enhancements.rb +0 -101
- data/lib/nanoc/filters.rb +0 -7
- data/lib/nanoc/filters/eruby_filter.rb +0 -39
- data/lib/nanoc/filters/haml_filter.rb +0 -18
- data/lib/nanoc/filters/liquid_filter.rb +0 -47
- data/lib/nanoc/filters/markaby_filter.rb +0 -15
- data/lib/nanoc/filters/markdown_filter.rb +0 -13
- data/lib/nanoc/filters/rdoc_filter.rb +0 -15
- data/lib/nanoc/filters/sass_filter.rb +0 -13
- data/lib/nanoc/filters/smartypants_filter.rb +0 -13
- data/lib/nanoc/filters/textile_filter.rb +0 -13
- data/lib/nanoc/page.rb +0 -171
- data/lib/nanoc/page_drop.rb +0 -18
- data/lib/nanoc/page_proxy.rb +0 -30
@@ -0,0 +1,19 @@
|
|
1
|
+
module Nanoc
|
2
|
+
class Plugin
|
3
|
+
|
4
|
+
class << self
|
5
|
+
attr_accessor :_identifiers
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.identifiers(*identifiers)
|
9
|
+
self._identifiers = [] unless instance_variable_defined?(:@_identifiers)
|
10
|
+
identifiers.empty? ? self._identifiers || [] : self._identifiers = (self._identifiers || []) + identifiers
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.identifier(identifier=nil)
|
14
|
+
self._identifiers = [] unless instance_variable_defined?(:@_identifiers)
|
15
|
+
identifier.nil? ? self.identifiers.first : self.identifiers(identifier)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Nanoc
|
2
|
+
class PluginManager
|
3
|
+
|
4
|
+
@@data_sources = {}
|
5
|
+
@@filters = {}
|
6
|
+
@@layout_processors = {}
|
7
|
+
|
8
|
+
def self.subclasses_of(superclass)
|
9
|
+
subclasses = []
|
10
|
+
ObjectSpace.each_object(Class) { |subclass| subclasses << subclass if subclass < superclass }
|
11
|
+
subclasses
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.data_source_named(name)
|
15
|
+
@@data_sources[name.to_sym] ||= subclasses_of(DataSource).find do |klass|
|
16
|
+
klass.identifiers.include?(name.to_sym)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.filter_named(name)
|
21
|
+
@@filters[name.to_sym] ||= subclasses_of(Filter).find do |klass|
|
22
|
+
klass.identifiers.include?(name.to_sym)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.layout_processor_for_extension(ext)
|
27
|
+
@@filters[ext.to_sym] ||= subclasses_of(LayoutProcessor).find do |klass|
|
28
|
+
klass.extensions.include?(ext)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Nanoc
|
2
|
+
class Site
|
3
|
+
|
4
|
+
DEFAULT_CONFIG = {
|
5
|
+
:output_dir => 'output',
|
6
|
+
:eruby_engine => 'erb',
|
7
|
+
:data_source => 'filesystem'
|
8
|
+
}
|
9
|
+
|
10
|
+
attr_reader :config
|
11
|
+
attr_reader :compiler, :data_source
|
12
|
+
attr_reader :code, :pages, :page_defaults, :layouts, :templates
|
13
|
+
|
14
|
+
# Creating a Site object
|
15
|
+
|
16
|
+
def self.from_cwd
|
17
|
+
File.file?('config.yaml') ? new : nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
# Load configuration
|
22
|
+
@config = DEFAULT_CONFIG.merge((YAML.load_file('config.yaml') || {}).clean)
|
23
|
+
|
24
|
+
# Create data source
|
25
|
+
@data_source_class = PluginManager.data_source_named(@config[:data_source])
|
26
|
+
error "Unrecognised data source: #{@config[:data_source]}" if @data_source_class.nil?
|
27
|
+
@data_source = @data_source_class.new(self)
|
28
|
+
|
29
|
+
# Create compiler
|
30
|
+
@compiler = Compiler.new(self)
|
31
|
+
@autocompiler = AutoCompiler.new(self)
|
32
|
+
|
33
|
+
# Set not loaded
|
34
|
+
@data_loaded = false
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_data(force=false)
|
38
|
+
return if @data_loaded and !force
|
39
|
+
|
40
|
+
# Load data
|
41
|
+
@data_source.loading do
|
42
|
+
@code = @data_source.code
|
43
|
+
@pages = @data_source.pages.map { |p| Page.new(p, self) }
|
44
|
+
@page_defaults = @data_source.page_defaults
|
45
|
+
@layouts = @data_source.layouts
|
46
|
+
@templates = @data_source.templates
|
47
|
+
end
|
48
|
+
|
49
|
+
# Setup child-parent links
|
50
|
+
@pages.each do |page|
|
51
|
+
# Skip pages without parent
|
52
|
+
next if page.path == '/'
|
53
|
+
|
54
|
+
# Get parent
|
55
|
+
parent_path = page.path.sub(/[^\/]+\/$/, '')
|
56
|
+
parent = @pages.find { |p| p.path == parent_path }
|
57
|
+
|
58
|
+
# Link
|
59
|
+
page.parent = parent
|
60
|
+
parent.children << page
|
61
|
+
end
|
62
|
+
|
63
|
+
# Set loaded
|
64
|
+
@data_loaded = true
|
65
|
+
end
|
66
|
+
|
67
|
+
# Creating a new site on disk
|
68
|
+
|
69
|
+
def self.create(sitename)
|
70
|
+
# Check whether site exists
|
71
|
+
error "A site named '#{sitename}' already exists." if File.exist?(sitename)
|
72
|
+
|
73
|
+
FileUtils.mkdir_p sitename
|
74
|
+
in_dir([sitename]) do
|
75
|
+
# Create output
|
76
|
+
FileManager.create_dir 'output'
|
77
|
+
|
78
|
+
# Create config
|
79
|
+
FileManager.create_file 'config.yaml' do
|
80
|
+
"output_dir: \"output\"\n" +
|
81
|
+
"data_source: \"filesystem\"\n"
|
82
|
+
end
|
83
|
+
|
84
|
+
# Create rakefile
|
85
|
+
FileManager.create_file 'Rakefile' do
|
86
|
+
"Dir['tasks/**/*.rake'].sort.each { |rakefile| load rakefile }\n" +
|
87
|
+
"\n" +
|
88
|
+
"task :default do\n" +
|
89
|
+
" puts 'This is an example rake task.'\n" +
|
90
|
+
"end\n"
|
91
|
+
end
|
92
|
+
|
93
|
+
# Create tasks
|
94
|
+
FileManager.create_file 'tasks/default.rake' do
|
95
|
+
"task :example do\n" +
|
96
|
+
" puts 'This is an example rake task in tasks/default.rake.'\n" +
|
97
|
+
"end\n"
|
98
|
+
end
|
99
|
+
|
100
|
+
# Setup site
|
101
|
+
Site.from_cwd.setup
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Compiling
|
106
|
+
|
107
|
+
def compile
|
108
|
+
@compiler.run
|
109
|
+
end
|
110
|
+
|
111
|
+
def autocompile(port)
|
112
|
+
@autocompiler.start(port)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Creating
|
116
|
+
|
117
|
+
def setup
|
118
|
+
@data_source.loading { @data_source.setup }
|
119
|
+
end
|
120
|
+
|
121
|
+
def create_page(name, template_name='default')
|
122
|
+
load_data
|
123
|
+
|
124
|
+
template = @templates.find { |t| t[:name] == template_name }
|
125
|
+
error "A template named '#{template_name}' was not found; aborting." if template.nil?
|
126
|
+
|
127
|
+
@data_source.loading { @data_source.create_page(name, template) }
|
128
|
+
end
|
129
|
+
|
130
|
+
def create_template(name)
|
131
|
+
load_data
|
132
|
+
|
133
|
+
@data_source.loading {@data_source.create_template(name) }
|
134
|
+
end
|
135
|
+
|
136
|
+
def create_layout(name)
|
137
|
+
load_data
|
138
|
+
|
139
|
+
@data_source.loading { @data_source.create_layout(name) }
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
begin ; require 'active_record' ; rescue LoadError ; end
|
2
|
+
|
3
|
+
module Nanoc::DataSource::Database
|
4
|
+
|
5
|
+
########## Helper classes ##########
|
6
|
+
|
7
|
+
begin
|
8
|
+
|
9
|
+
class DatabasePage < ActiveRecord::Base
|
10
|
+
set_table_name 'pages'
|
11
|
+
end
|
12
|
+
|
13
|
+
class DatabasePageDefault < ActiveRecord::Base
|
14
|
+
set_table_name 'page_defaults'
|
15
|
+
end
|
16
|
+
|
17
|
+
class DatabaseTemplate < ActiveRecord::Base
|
18
|
+
set_table_name 'templates'
|
19
|
+
end
|
20
|
+
|
21
|
+
class DatabaseLayout < ActiveRecord::Base
|
22
|
+
set_table_name 'layouts'
|
23
|
+
end
|
24
|
+
|
25
|
+
class DatabaseCodePiece < ActiveRecord::Base
|
26
|
+
set_table_name 'code_pieces'
|
27
|
+
end
|
28
|
+
|
29
|
+
rescue NameError
|
30
|
+
end
|
31
|
+
|
32
|
+
class DatabaseDataSource < Nanoc::DataSource
|
33
|
+
|
34
|
+
########## Attributes ##########
|
35
|
+
|
36
|
+
identifier :database
|
37
|
+
|
38
|
+
########## Preparation ##########
|
39
|
+
|
40
|
+
def up
|
41
|
+
nanoc_require 'active_record'
|
42
|
+
|
43
|
+
# Connect to the database
|
44
|
+
ActiveRecord::Base.establish_connection(@site.config[:database])
|
45
|
+
end
|
46
|
+
|
47
|
+
def down
|
48
|
+
# Disconnect from the database
|
49
|
+
ActiveRecord::Base.remove_connection
|
50
|
+
end
|
51
|
+
|
52
|
+
def setup
|
53
|
+
# Create tables
|
54
|
+
schema = ActiveRecord::Schema
|
55
|
+
schema.verbose = false
|
56
|
+
schema.define do
|
57
|
+
|
58
|
+
create_table :pages, :force => true do |t|
|
59
|
+
t.column :content, :text
|
60
|
+
t.column :path, :string
|
61
|
+
t.column :meta, :text
|
62
|
+
end
|
63
|
+
|
64
|
+
create_table :page_defaults, :force => true do |t|
|
65
|
+
t.column :meta, :text
|
66
|
+
end
|
67
|
+
|
68
|
+
create_table :layouts, :force => true do |t|
|
69
|
+
t.column :name, :string
|
70
|
+
t.column :content, :text
|
71
|
+
t.column :extension, :string
|
72
|
+
end
|
73
|
+
|
74
|
+
create_table :templates, :force => true do |t|
|
75
|
+
t.column :content, :text
|
76
|
+
t.column :name, :string
|
77
|
+
t.column :meta, :text
|
78
|
+
end
|
79
|
+
|
80
|
+
create_table :code_pieces, :force => true do |t|
|
81
|
+
t.column :name, :string
|
82
|
+
t.column :code, :text
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
# Create first page
|
88
|
+
DatabasePage.create(
|
89
|
+
:path => '/',
|
90
|
+
:content => "I'm a brand new root page. Please edit me!\n",
|
91
|
+
:meta => "# Built-in\n" +
|
92
|
+
"\n" +
|
93
|
+
"# Custom\n" +
|
94
|
+
"title: \"A New Root Page\"\n"
|
95
|
+
)
|
96
|
+
|
97
|
+
# Create page defaults
|
98
|
+
DatabasePageDefault.create(
|
99
|
+
:meta => "# Built-in\n" +
|
100
|
+
"custom_path: none\n" +
|
101
|
+
"extension: \"html\"\n" +
|
102
|
+
"filename: \"index\"\n" +
|
103
|
+
"filters_post: []\n" +
|
104
|
+
"filters_pre: []\n" +
|
105
|
+
"is_draft: false\n" +
|
106
|
+
"layout: \"default\"\n" +
|
107
|
+
"skip_output: false\n" +
|
108
|
+
"\n" +
|
109
|
+
"# Custom\n"
|
110
|
+
)
|
111
|
+
|
112
|
+
# Create default layout
|
113
|
+
DatabaseLayout.create(
|
114
|
+
:name => 'default',
|
115
|
+
:content => "<html>\n" +
|
116
|
+
" <head>\n" +
|
117
|
+
" <title><%= @page.title %></title>\n" +
|
118
|
+
" </head>\n" +
|
119
|
+
" <body>\n" +
|
120
|
+
"<%= @page.content %>\n" +
|
121
|
+
" </body>\n" +
|
122
|
+
"</html>",
|
123
|
+
:extension => '.erb'
|
124
|
+
)
|
125
|
+
|
126
|
+
# Create default template
|
127
|
+
DatabaseTemplate.create(
|
128
|
+
:name => 'default',
|
129
|
+
:content => 'Hi, I\'m a new page!',
|
130
|
+
:meta => "# Built-in\n" +
|
131
|
+
"\n" +
|
132
|
+
"# Custom\n" +
|
133
|
+
"title: \"A New Page\"\n"
|
134
|
+
)
|
135
|
+
|
136
|
+
# Create default code piece
|
137
|
+
DatabaseCodePiece.create(
|
138
|
+
:name => 'default',
|
139
|
+
:code => "def html_escape(str)\n" +
|
140
|
+
" str.gsub('&', '&').str('<', '<').str('>', '>').str('\"', '"')\n" +
|
141
|
+
"end\n" +
|
142
|
+
"alias h html_escape\n"
|
143
|
+
)
|
144
|
+
|
145
|
+
log(:high, "Set up database schema.")
|
146
|
+
end
|
147
|
+
|
148
|
+
########## Loading data ##########
|
149
|
+
|
150
|
+
def pages
|
151
|
+
# Create Pages for each database object
|
152
|
+
DatabasePage.find(:all).inject([]) do |pages, page|
|
153
|
+
# Read metadata
|
154
|
+
hash = (YAML.load(page.meta || '') || {}).clean
|
155
|
+
|
156
|
+
if hash[:is_draft]
|
157
|
+
# Skip drafts
|
158
|
+
pages
|
159
|
+
else
|
160
|
+
# Get extra info
|
161
|
+
extras = { :path => page.path, :uncompiled_content => page.content }
|
162
|
+
|
163
|
+
# Add to list of pages
|
164
|
+
pages + [ hash.merge(extras) ]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def page_defaults
|
170
|
+
YAML.load(DatabasePageDefault.find(:first).meta) || {}
|
171
|
+
end
|
172
|
+
|
173
|
+
def layouts
|
174
|
+
DatabaseLayout.find(:all).map do |layout|
|
175
|
+
{
|
176
|
+
:name => layout.name,
|
177
|
+
:content => layout.content,
|
178
|
+
:extension => layout.extension
|
179
|
+
}
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def templates
|
184
|
+
DatabaseTemplate.find(:all).map do |template|
|
185
|
+
{
|
186
|
+
:name => template.name,
|
187
|
+
:content => template.content,
|
188
|
+
:meta => template.meta
|
189
|
+
}
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def code
|
194
|
+
DatabaseCodePiece.find(:all).map { |p| p.code }.join("\n")
|
195
|
+
end
|
196
|
+
|
197
|
+
########## Creating data ##########
|
198
|
+
|
199
|
+
def create_page(path, template)
|
200
|
+
# Make sure path does not start or end with a slash
|
201
|
+
sanitized_path = ('/' + path + '/').gsub(/^\/+|\/+$/, '/')
|
202
|
+
|
203
|
+
# Make sure the page doesn't exist yet
|
204
|
+
unless DatabasePage.find_all_by_path(sanitized_path).empty?
|
205
|
+
error "A page named '#{sanitized_path}' already exists."
|
206
|
+
end
|
207
|
+
|
208
|
+
# Create page
|
209
|
+
DatabasePage.create(
|
210
|
+
:path => sanitized_path,
|
211
|
+
:content => "I'm a brand new page. Please edit me!\n",
|
212
|
+
:meta => "# Built-in\n\n# Custom\ntitle: A New Page\n"
|
213
|
+
)
|
214
|
+
|
215
|
+
log(:high, "Created page '#{sanitized_path}'.")
|
216
|
+
end
|
217
|
+
|
218
|
+
def create_layout(name)
|
219
|
+
# Make sure the layout doesn't exist yet
|
220
|
+
unless DatabaseLayout.find_all_by_name(name).empty?
|
221
|
+
error "A layout named '#{name}' already exists."
|
222
|
+
end
|
223
|
+
|
224
|
+
# Create layout
|
225
|
+
DatabaseLayout.create(
|
226
|
+
:name => name,
|
227
|
+
:content => "<html>\n" +
|
228
|
+
" <head>\n" +
|
229
|
+
" <title><%= @page.title %></title>\n" +
|
230
|
+
" </head>\n" +
|
231
|
+
" <body>\n" +
|
232
|
+
"<%= @page.content %>\n" +
|
233
|
+
" </body>\n" +
|
234
|
+
"</html>",
|
235
|
+
:extension => '.erb'
|
236
|
+
)
|
237
|
+
|
238
|
+
log(:high, "Created layout '#{name}'.")
|
239
|
+
end
|
240
|
+
|
241
|
+
def create_template(name)
|
242
|
+
# Make sure the layout doesn't exist yet
|
243
|
+
unless DatabaseTemplate.find_all_by_name(name).empty?
|
244
|
+
error "A template named '#{name}' already exists."
|
245
|
+
end
|
246
|
+
|
247
|
+
# Create template
|
248
|
+
DatabaseTemplate.create(
|
249
|
+
:name => name,
|
250
|
+
:content => "Hi, I'm a brand new page!\n",
|
251
|
+
:meta => "# Built-in\n\n# Custom\ntitle: A New Page\n"
|
252
|
+
)
|
253
|
+
|
254
|
+
log(:high,"Created template '#{name}'.")
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|