nanoc 1.6.2 → 2.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.
- 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
|