plate 0.5.4 → 0.6.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.md +15 -0
- data/README.md +21 -19
- data/Rakefile +9 -5
- data/lib/plate.rb +16 -11
- data/lib/plate/asset.rb +86 -23
- data/lib/plate/builder.rb +286 -150
- data/lib/plate/callbacks.rb +20 -4
- data/lib/plate/cli.rb +65 -53
- data/lib/plate/dsl.rb +68 -0
- data/lib/plate/errors.rb +7 -4
- data/lib/plate/hash_proxy.rb +27 -0
- data/lib/plate/helpers/url_helper.rb +19 -0
- data/lib/plate/layout.rb +41 -36
- data/lib/plate/less_template.rb +34 -0
- data/lib/plate/page.rb +108 -84
- data/lib/plate/sass_template.rb +15 -12
- data/lib/plate/site.rb +31 -1
- data/lib/plate/version.rb +1 -1
- data/lib/plate/view.rb +30 -11
- data/lib/templates/bootstrap.min.css +241 -0
- data/lib/templates/callbacks.rb +37 -0
- data/lib/templates/config.yml +9 -1
- data/lib/templates/index.html +37 -0
- data/lib/templates/layout.html.erb +41 -0
- data/lib/templates/rss.erb +32 -0
- metadata +53 -17
- data/lib/templates/index.md +0 -6
- data/lib/templates/layout.erb +0 -12
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
## Plate 0.6.0 (Unreleased)
|
2
|
+
|
3
|
+
* Meta data can be read from the config file and used to store common site data such as title, description, author, etc.
|
4
|
+
* Modified new site generator to use meta data structure
|
5
|
+
* Added RSS feed as part of new site generation
|
6
|
+
* Added helper for making relative links absolute for use within RSS feeds
|
7
|
+
* Documentation updates
|
8
|
+
* Added a new DSL for callbacks and other extensions. Files within `./lib` will now be evaluated using the DSL instead of just being required.
|
9
|
+
* Helpers have been moved to a separate directory in the site source root named `./helpers`.
|
10
|
+
* Added new site template with some base CSS styles and helpful information to get started.
|
11
|
+
* Allow a build destination to be set within the config.yml file.
|
12
|
+
* Allow customization of Sass output style with config variable :sass_style
|
13
|
+
* Added DSL methods to register asset and template engines.
|
14
|
+
* Added engine for Less CSS processing. Install the less gem to use this engine.
|
15
|
+
|
1
16
|
## Plate 0.5.4
|
2
17
|
|
3
18
|
* Added config option to command line utility to load a specific config file.
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](http://travis-ci.org/jdtornow/plate) [](https://gemnasium.com/jdtornow/plate)
|
4
4
|
|
5
|
-
Plate is a super simple static site generator and blog engine. At its core, it takes a folder full of Markdown files and turns it into a static HTML site that you can host anywhere.
|
5
|
+
Plate is a super simple static site generator and blog engine. At its core, it takes a folder full of Markdown files and turns it into a static HTML site that you can host anywhere.
|
6
6
|
|
7
7
|
In addition to basic formatting with Markdown, Plate also supports generating more dynamic files with ERB or HAML, and compiling asset files with CoffeeScript, Sass and others.
|
8
8
|
|
@@ -25,39 +25,35 @@ Plate is designed to be a command-line utility. Here is a rundown of some of the
|
|
25
25
|
To generate a new site with plate, run the following command:
|
26
26
|
|
27
27
|
platify .
|
28
|
-
|
28
|
+
|
29
29
|
Or, with the normal `plate` command:
|
30
30
|
|
31
31
|
plate new .
|
32
|
-
|
32
|
+
|
33
33
|
### Building a site
|
34
|
-
|
34
|
+
|
35
35
|
To build your site, run:
|
36
36
|
|
37
37
|
plate build
|
38
|
-
|
38
|
+
|
39
39
|
Or, just run `plate` without any options to build the site.
|
40
40
|
|
41
41
|
plate
|
42
|
-
|
42
|
+
|
43
43
|
To show details about the site build, enable verbose mode:
|
44
44
|
|
45
45
|
plate --verbose
|
46
|
-
|
47
|
-
When writing a post, it is sometimes helpful to watch the site for changes and just reload content after each file save. To enable this mode, run the build command with the `--watch` option. Every time a file is saved, the utility will rebuild that file so you can simply hit refresh in your browser to see the changes without rebuilding the entire site.
|
48
46
|
|
49
|
-
plate --watch
|
50
|
-
|
51
47
|
### Creating a new post
|
52
|
-
|
48
|
+
|
53
49
|
To create a new blog post with the default options, run:
|
54
50
|
|
55
51
|
plate post "New Post Name"
|
56
|
-
|
52
|
+
|
57
53
|
You can also put default post options (such as a category or layout) into the command line:
|
58
54
|
|
59
55
|
plate post "New Post Name" --category Articles --layout post
|
60
|
-
|
56
|
+
|
61
57
|
Or, if you always use the same default category and/or layout, you can put those options into your config file instead like so:
|
62
58
|
|
63
59
|
# In config/plate.yml
|
@@ -73,14 +69,15 @@ Plate observes the following folder structure in your site:
|
|
73
69
|
|
74
70
|
* config/ - Put your global configuration settings here.
|
75
71
|
* content/ - All custom content for the site, besides blog posts. Everything in this folder will be copied over to the published site.
|
72
|
+
* helpers/ - Helpers are loaded into views automatically and can be used within dynamically rendered pages using a template language. (Such as Erb or Haml)
|
76
73
|
* layouts/ - Global layouts available for use on all content pages and posts.
|
77
|
-
* lib/ - Extend the basic functionality of Plate with plugins in this directory. All `.rb` files
|
74
|
+
* lib/ - Extend the basic functionality of Plate with plugins in this directory. All `.rb` files run against the Plate DSL.
|
78
75
|
* posts/ - All blog post content for the site. Posts can be organized into sub-directories if you like.
|
79
76
|
* public/ - This will be generated if it does not exist, contains the produced site. Set this as the web server root to your site for development mode.
|
80
77
|
|
81
78
|
## Extending Plate
|
82
79
|
|
83
|
-
Plate is meant to be extended easily. You might want to extend the basic functionality of Plate to add additional functionality for your site. To get started, create a directory named `lib` in the root of your site. Any Ruby files (ending in `.rb`) will be automatically loaded into the stack when Plate is run.
|
80
|
+
Plate is meant to be extended easily. You might want to extend the basic functionality of Plate to add additional functionality for your site. To get started, create a directory named `lib` in the root of your site. Any Ruby files (ending in `.rb`) will be automatically loaded into the stack when Plate is run.
|
84
81
|
|
85
82
|
### Callbacks
|
86
83
|
|
@@ -89,28 +86,33 @@ Callbacks are used to call certain blocks of code when an event happens in the l
|
|
89
86
|
The callbacks currently available are:
|
90
87
|
|
91
88
|
* Site - `before_render`, `after_render`
|
92
|
-
* Page/Post - `before_render`, `after_render`
|
89
|
+
* Page/Post - `before_render`, `after_render`, `before_write`, `after_write`
|
90
|
+
* Asset - `before_render`, `after_render`, `before_write`, `after_write`
|
93
91
|
|
94
92
|
Example of a callback to be run when a site completes the build:
|
95
93
|
|
96
|
-
|
94
|
+
register_callback :site, :after_render do |site|
|
97
95
|
puts "the site finished rendering!"
|
98
96
|
end
|
99
97
|
|
98
|
+
Put the above snippet in any file in the lib folder, and it will be registered when your site is compiled.
|
99
|
+
|
100
100
|
### Helpers
|
101
101
|
|
102
102
|
Helpers are modules that are automatically loaded into views. Any methods in the module will be available when you render a page.
|
103
103
|
|
104
|
-
An example of a helper file located in `
|
104
|
+
An example of a helper file located in `helpers/sample_helper.rb`
|
105
105
|
|
106
106
|
module SampleHelper
|
107
107
|
def sample_helper_method
|
108
108
|
"yes"
|
109
109
|
end
|
110
110
|
end
|
111
|
-
|
111
|
+
|
112
112
|
Then, in your `.erb` view you can call `sample_helper_method`.
|
113
113
|
|
114
|
+
All files in the `helpers/` directory are assumed to be helper modules and will be loaded automatically at runtime.
|
115
|
+
|
114
116
|
## Full Documentation
|
115
117
|
|
116
118
|
View the [full documentation on rdoc.info](http://rdoc.info/gems/plate/frames)
|
data/Rakefile
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'rake'
|
3
|
-
require 'yard'
|
4
3
|
|
5
4
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), *%w(lib)))
|
6
5
|
|
@@ -17,11 +16,16 @@ end
|
|
17
16
|
namespace :test do
|
18
17
|
desc "Build the sample site and leave its contents in test/sample/public"
|
19
18
|
task :sample do
|
20
|
-
sh %q(
|
19
|
+
sh %q(turn -I"lib:test" test/test_builder.rb -n /sample/)
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
24
|
-
|
25
|
-
|
23
|
+
begin
|
24
|
+
require 'yard'
|
25
|
+
|
26
|
+
YARD::Rake::YardocTask.new do |t|
|
27
|
+
end
|
26
28
|
|
27
|
-
task :doc => :yard
|
29
|
+
task :doc => :yard
|
30
|
+
rescue LoadError
|
31
|
+
end
|
data/lib/plate.rb
CHANGED
@@ -10,44 +10,49 @@ require 'plate/errors'
|
|
10
10
|
|
11
11
|
module Plate
|
12
12
|
autoload :CLI, 'plate/cli'
|
13
|
-
|
13
|
+
autoload :Dsl, 'plate/dsl'
|
14
|
+
|
14
15
|
autoload :Builder, 'plate/builder'
|
15
16
|
autoload :Callbacks, 'plate/callbacks'
|
16
17
|
autoload :Layout, 'plate/layout'
|
17
18
|
autoload :Site, 'plate/site'
|
18
|
-
|
19
|
+
|
19
20
|
autoload :BloggingHelper, 'plate/helpers/blogging_helper'
|
20
21
|
autoload :MetaHelper, 'plate/helpers/meta_helper'
|
21
22
|
autoload :URLHelper, 'plate/helpers/url_helper'
|
22
|
-
|
23
|
+
|
23
24
|
autoload :View, 'plate/view'
|
24
|
-
|
25
|
+
|
25
26
|
autoload :PostCollection, 'plate/post_collection'
|
26
|
-
|
27
|
+
autoload :HashProxy, 'plate/hash_proxy'
|
28
|
+
|
27
29
|
autoload :Asset, 'plate/asset'
|
28
30
|
autoload :DynamicPage, 'plate/dynamic_page'
|
29
31
|
autoload :Page, 'plate/page'
|
30
32
|
autoload :Post, 'plate/post'
|
31
33
|
autoload :StaticPage, 'plate/static_page'
|
32
|
-
|
33
|
-
autoload :Engine, 'plate/engine'
|
34
|
+
|
35
|
+
autoload :Engine, 'plate/engine'
|
36
|
+
|
34
37
|
autoload :HamlTemplate, 'plate/haml_template'
|
38
|
+
autoload :LessTemplate, 'plate/less_template'
|
35
39
|
autoload :MarkdownTemplate, 'plate/markdown_template'
|
36
40
|
autoload :SassTemplate, 'plate/sass_template'
|
37
41
|
autoload :ScssTemplate, 'plate/scss_template'
|
38
|
-
|
42
|
+
|
39
43
|
extend Engine
|
40
44
|
@engines ||= {}
|
41
|
-
|
45
|
+
|
42
46
|
# Set up the basic engines that are supported by Plate. Add your own this same way.
|
43
47
|
# Thanks to sprockets for the inspiration.
|
44
48
|
# https://github.com/sstephenson/sprockets
|
45
|
-
|
49
|
+
|
46
50
|
# Assets
|
47
51
|
register_asset_engine :coffee, Tilt::CoffeeScriptTemplate
|
52
|
+
register_asset_engine :less, LessTemplate
|
48
53
|
register_asset_engine :sass, SassTemplate
|
49
54
|
register_asset_engine :scss, ScssTemplate
|
50
|
-
|
55
|
+
|
51
56
|
# Layouts & Markup
|
52
57
|
register_template_engine :erb, Tilt::ERBTemplate
|
53
58
|
register_template_engine :haml, HamlTemplate
|
data/lib/plate/asset.rb
CHANGED
@@ -1,50 +1,113 @@
|
|
1
1
|
module Plate
|
2
|
-
# An asset is a
|
3
|
-
# to the destination.
|
2
|
+
# An asset is a dynamic stylesheet or javascript file that needs some processing before it is ready
|
3
|
+
# to be placed into the site's destination directory.
|
4
|
+
#
|
5
|
+
# Some examples of supported asset formats are [CoffeeScript](http://coffeescript.org)
|
6
|
+
# and [Sass](http://sass-lang.com).
|
7
|
+
#
|
8
|
+
# # Asset Engines
|
9
|
+
#
|
10
|
+
# Assets are compiled based on the file extensions of the source file, and the so-called "engines"
|
11
|
+
# that are available and registered to Plate. By default, Plate does not require any of these
|
12
|
+
# asset engines to run, so any external dependencies will need to be installed before attempting
|
13
|
+
# to use an asset engine.
|
14
|
+
#
|
15
|
+
# For example, in order to use the CoffeeScript engine (and thus a file with the extension of
|
16
|
+
# `.coffee`) the CoffeeScript gem will need to be installed. (`gem install coffee-script`).
|
17
|
+
#
|
18
|
+
# # File Naming
|
19
|
+
#
|
20
|
+
# For best results, name your source files exactly how you would like them to be displayed
|
21
|
+
# once compiled.
|
22
|
+
#
|
23
|
+
# To create a file in the destination named site.js that is compiled from a CoffeeScript source,
|
24
|
+
# name the file in the source directory site.js.coffee. The asset compilation engine will
|
25
|
+
# stop trying to render the file with an asset engine when it finds the first non-registered
|
26
|
+
# file extension. In this case, "js" is not recognized as an asset engine extension that needs
|
27
|
+
# compilation, so compilation stops and the file is written to the destination.
|
4
28
|
class Asset < Page
|
29
|
+
# The engines in use for this asset. Engines are determined by looping through
|
30
|
+
# the extensions of the base file and associating those with a registered engine.
|
31
|
+
#
|
32
|
+
# @return [Array] List of engines in use for this file.
|
5
33
|
def engines
|
6
34
|
@engines ||= self.extensions.reverse.collect { |e| self.site.registered_asset_engines[e.gsub(/\./, '').to_sym] }.reject { |e| !e }
|
7
35
|
end
|
8
|
-
|
36
|
+
|
37
|
+
# The file extensions for this asset's source file.
|
38
|
+
#
|
39
|
+
# @return [Array] List of extensions
|
9
40
|
def extensions
|
10
41
|
@extensions ||= self.basename.scan(/\.[^.]+/)
|
11
42
|
end
|
12
|
-
|
43
|
+
|
44
|
+
# The end result format for this file. This is the first file extension in the asset's
|
45
|
+
# source file name that is not a registered engine extension.
|
46
|
+
#
|
47
|
+
# For example, a file named `screen.css.css` will have a format extension of "css".
|
48
|
+
#
|
49
|
+
# @return [String]
|
13
50
|
def format_extension
|
14
51
|
self.extensions.reverse.detect { |e| !self.site.asset_engine_extensions.include?(e) }
|
15
52
|
end
|
16
|
-
|
17
|
-
|
18
|
-
|
53
|
+
|
54
|
+
# The destination file path for this asset.
|
55
|
+
#
|
56
|
+
# @return [String]
|
57
|
+
def file_path
|
58
|
+
[ directory, file_name ].join('/')
|
19
59
|
end
|
20
|
-
|
60
|
+
|
61
|
+
# Directory name for the source asset file
|
62
|
+
#
|
63
|
+
# @return [String]
|
21
64
|
def pathname
|
22
65
|
File.dirname(self.file)
|
23
66
|
end
|
24
|
-
|
25
|
-
#
|
67
|
+
|
68
|
+
# Generates the resulting content for this asset using the `engines` determined
|
69
|
+
# by the source file's name. Unlike a page, assets do not render content
|
70
|
+
# using a layout.
|
71
|
+
#
|
72
|
+
# @return [String]
|
26
73
|
def rendered_content
|
27
74
|
return @rendered_content if @rendered_content
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
75
|
+
|
76
|
+
around_callback :render do
|
77
|
+
result = File.read(file)
|
78
|
+
|
79
|
+
self.engines.each do |engine|
|
80
|
+
template = engine.new() { result }
|
81
|
+
result = template.render(self, :site => self.site)
|
82
|
+
end
|
83
|
+
|
84
|
+
@rendered_content = result
|
34
85
|
end
|
35
|
-
|
36
|
-
@rendered_content
|
86
|
+
|
87
|
+
@rendered_content
|
37
88
|
end
|
38
|
-
|
39
|
-
# Write this
|
40
|
-
# in
|
89
|
+
|
90
|
+
# Write this asset file to the destination. The content is written to disk using the path
|
91
|
+
# designated in {#file_path} and the content from {#rendered_content}.
|
92
|
+
#
|
93
|
+
# The callbacks `before_write` and `after_write` are included here. To perform
|
94
|
+
# custom actions before or after an asset file is written to disk, use these callback
|
95
|
+
# methods.
|
96
|
+
#
|
97
|
+
# See {Plate::Callbacks} for more information on setting up callbacks.
|
98
|
+
#
|
99
|
+
# @return [String] The file path that was written to.
|
41
100
|
def write!
|
42
101
|
path = File.join(site.build_destination, file_path)
|
43
102
|
FileUtils.mkdir_p(File.dirname(path))
|
44
103
|
|
45
|
-
|
46
|
-
|
104
|
+
around_callback :write do
|
105
|
+
File.open(path, 'w') do |f|
|
106
|
+
f.write(self.rendered_content)
|
107
|
+
end
|
47
108
|
end
|
109
|
+
|
110
|
+
path
|
48
111
|
end
|
49
112
|
end
|
50
113
|
end
|
data/lib/plate/builder.rb
CHANGED
@@ -1,257 +1,357 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'digest'
|
3
3
|
|
4
|
-
module Plate
|
5
|
-
#
|
4
|
+
module Plate
|
5
|
+
# The builder is used to compile create a site from a source directory and output it to a destination.
|
6
|
+
#
|
7
|
+
# In most cases, the Builder is called directly from the command line tools (see {Plate::CLI}) and does
|
8
|
+
# not need to be called directly.
|
6
9
|
class Builder
|
7
|
-
|
8
|
-
|
10
|
+
include Callbacks
|
11
|
+
|
12
|
+
# @return [String] The destination directory path where the site should be placed upon a completed build.
|
13
|
+
attr_accessor :destination
|
14
|
+
|
15
|
+
# @return [Boolean] Is logging enabled for this build? Set using the `--verbose` command line option.
|
16
|
+
attr_accessor :enable_logging
|
17
|
+
|
18
|
+
# @return [Hash] A hash of meta values loaded from the config file in the site's source.
|
19
|
+
attr_accessor :metadata
|
20
|
+
|
21
|
+
# @return [Hash] The options hash for this site build.
|
22
|
+
attr_accessor :options
|
23
|
+
|
24
|
+
# @return [Site] The site instance for this build.
|
25
|
+
attr_accessor :site
|
26
|
+
|
27
|
+
# @return [String] The source directory path where the site's content is loaded from.
|
28
|
+
attr_accessor :source
|
29
|
+
|
30
|
+
# @return [Array] A list of helper classes loaded from the `source/helpers` directory.
|
31
|
+
attr_reader :helpers
|
32
|
+
|
33
|
+
# Create a new instance of the builder for the given site source, destination
|
34
|
+
# and options.
|
35
|
+
#
|
36
|
+
# In most cases this should be initialized from the command line utilities.
|
9
37
|
def initialize(source, destination, options = {})
|
10
38
|
@source = source
|
11
39
|
@destination = destination
|
12
|
-
@
|
40
|
+
@metadata = {}
|
41
|
+
@options = Hash === options ? options.clone : {}
|
13
42
|
@options.symbolize_keys!
|
14
43
|
end
|
15
|
-
|
44
|
+
|
45
|
+
# The directory where the build details are cached. Caching is used to store
|
46
|
+
# temporary data, and as a temporary spot to build the site in before it is
|
47
|
+
# moved to the destination directory.
|
48
|
+
#
|
49
|
+
# Caching is probably a bad name for this since nothing is truly "cached", it is
|
50
|
+
# merely a temporary directory.
|
51
|
+
#
|
52
|
+
# The directory can be set in a site options, or uses the default which is in the user's
|
53
|
+
# current home directory under ~/.plate...
|
54
|
+
#
|
55
|
+
# @return [String]
|
16
56
|
def cache_location
|
17
57
|
return @cache_location if @cache_location
|
18
|
-
|
58
|
+
|
19
59
|
if self.options.has_key?(:cache_location)
|
20
60
|
@cache_location ||= File.expand_path(self.options[:cache_location])
|
21
61
|
else
|
22
62
|
@cache_location ||= File.expand_path("~/.plate/#{self.id}")
|
23
|
-
end
|
63
|
+
end
|
24
64
|
end
|
25
|
-
|
26
|
-
# Remove any caches from this site build
|
27
|
-
# temporary build folders so they can be reset
|
65
|
+
|
66
|
+
# Remove any caches and temporary data from this site build.
|
28
67
|
def clear_cache!
|
29
68
|
FileUtils.rm_rf(cache_location)
|
30
|
-
|
69
|
+
|
31
70
|
@cache_location = nil
|
32
71
|
@tmp_destination = nil
|
33
72
|
@loaded = false
|
34
73
|
end
|
35
|
-
|
36
|
-
#
|
74
|
+
|
75
|
+
# Loads the site, if not already loaded and returns the options listed.
|
76
|
+
#
|
77
|
+
# @return [Hash]
|
37
78
|
def config
|
38
|
-
|
79
|
+
load!
|
39
80
|
@options
|
40
81
|
end
|
41
|
-
|
42
|
-
# A unique id for this site, based off of the source directory
|
82
|
+
|
83
|
+
# A unique id for this site, based off of the source directory.
|
84
|
+
#
|
85
|
+
# @return [String]
|
43
86
|
def id
|
44
87
|
check_source!
|
45
|
-
|
46
88
|
@id ||= [ File.basename(source), Digest::MD5.hexdigest(source) ].collect { |s| s.to_s.downcase.parameterize }.join('-')
|
47
89
|
end
|
48
|
-
|
90
|
+
|
91
|
+
# Are there any items loaded within this site build? Returns false if there are no items.
|
92
|
+
#
|
93
|
+
# When builing a site from the source directory and there are no items persent, the builder
|
94
|
+
# will exit.
|
95
|
+
#
|
96
|
+
# @return [Boolean]
|
49
97
|
def items?
|
50
98
|
self.total_items > 0
|
51
99
|
end
|
52
|
-
|
100
|
+
|
101
|
+
# Read the source directory from the file system, configure the {Plate::Site} instance,
|
102
|
+
# and load any helpers and plugins.
|
103
|
+
#
|
104
|
+
# The loaded data is cached so this method can safely be called multiple times without
|
105
|
+
# having to read from the filesystem more than once.
|
106
|
+
#
|
107
|
+
# @return [Boolean] Was the source loaded?
|
53
108
|
def load!
|
54
109
|
unless @loaded
|
55
110
|
log('Site builder initialized.')
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
111
|
+
|
112
|
+
require_plugins!
|
113
|
+
load_config_file!
|
114
|
+
setup_site!
|
115
|
+
setup_tmp_directory!
|
116
|
+
|
62
117
|
@loaded = true
|
63
118
|
end
|
64
|
-
|
119
|
+
|
65
120
|
@loaded
|
66
121
|
end
|
67
|
-
|
122
|
+
|
123
|
+
# Utility method to grab the relative path of a specific file from the site's source.
|
124
|
+
#
|
125
|
+
# @return [String] File path relative to source directory.
|
68
126
|
def relative_path(file_or_directory)
|
69
127
|
file_or_directory.gsub(/^#{Regexp.quote(source)}(.*)$/, '\1')
|
70
128
|
end
|
71
|
-
|
129
|
+
|
130
|
+
# Rebuilds the entire site from source. Used when watching a directory for changes.
|
131
|
+
#
|
132
|
+
# @return [Boolean]
|
72
133
|
def rebuild!
|
73
134
|
log('Re-rendering site...')
|
74
|
-
|
135
|
+
|
75
136
|
clear_cache!
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
137
|
+
|
138
|
+
site.reload!
|
139
|
+
render_site!
|
140
|
+
copy_to_destination!
|
141
|
+
|
81
142
|
true
|
82
143
|
end
|
83
|
-
|
144
|
+
|
84
145
|
# When watching a directory for changes, allow reloading of site content based on modifications
|
85
146
|
# only in the content, layouts and posts folder. Changes to config or lib files will need to
|
86
147
|
# be reloaded manually.
|
148
|
+
#
|
149
|
+
# @return [Boolean]
|
87
150
|
def reloadable?(relative_file)
|
88
151
|
relative_file =~ /^\/?(content|layouts|posts)\/(.*?)/
|
89
152
|
end
|
90
|
-
|
91
|
-
#
|
153
|
+
|
154
|
+
# Start the rendering of the site based on the provided, source, destination and
|
155
|
+
# config options.
|
156
|
+
#
|
157
|
+
# The site will be loaded if it has not already been read from source, and rendered first to
|
158
|
+
# the temporary {#cache_location}
|
159
|
+
#
|
160
|
+
# @return [Boolean]
|
92
161
|
def render!
|
93
162
|
@start_time = Time.now
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
163
|
+
|
164
|
+
around_callback :render do
|
165
|
+
log("Building full site...")
|
166
|
+
|
167
|
+
load!
|
168
|
+
render_site!
|
169
|
+
copy_to_destination!
|
170
|
+
|
171
|
+
@end_time = Time.now
|
172
|
+
|
173
|
+
log("Site build completed in #{timer} seconds")
|
174
|
+
end
|
175
|
+
|
105
176
|
true
|
106
177
|
end
|
107
|
-
|
178
|
+
|
179
|
+
# Render a single file from the source to destination directories. This is used
|
180
|
+
# when watching a site for changes and recompiling a specific file on demand.
|
181
|
+
#
|
182
|
+
# For layout files, the entire site is reloaded. For all other files, only that file
|
183
|
+
# and corresponding destination file are reloaded.
|
184
|
+
#
|
185
|
+
# This may produce some unexpected behavior due to plugins and helpers not being reloaded and
|
186
|
+
# is still considered experimental.
|
187
|
+
#
|
188
|
+
# @return [Boolean]
|
108
189
|
def render_file!(relative_file_path)
|
109
|
-
|
110
|
-
|
111
|
-
page =
|
112
|
-
|
190
|
+
load!
|
191
|
+
|
192
|
+
page = site.find(relative_file_path)
|
193
|
+
|
194
|
+
# Ensure that the file path it is trying to reload actually exists
|
113
195
|
if page and page.file?
|
114
|
-
#
|
196
|
+
# If the file is a layout, rebuild all pages using it
|
115
197
|
if Layout === page
|
116
198
|
page.reload!
|
117
|
-
|
199
|
+
|
118
200
|
log("Building layout [#{page.relative_file}]")
|
119
|
-
|
120
|
-
|
201
|
+
|
202
|
+
# Rebuild all pages using that particular layout.
|
203
|
+
site.find_by_layout(page.relative_file).each do |layout_page|
|
121
204
|
self.render_file!(layout_page.relative_file)
|
122
205
|
end
|
123
206
|
else
|
124
207
|
log("Building file [#{page.relative_file}]")
|
125
|
-
|
126
|
-
#
|
208
|
+
|
209
|
+
# Does the file have an existing temporary file in the {#cache_location}
|
127
210
|
existing_tmp = File.join(tmp_destination, page.file_path)
|
128
|
-
|
211
|
+
|
129
212
|
if File.exists?(existing_tmp)
|
130
213
|
FileUtils.rm_rf(existing_tmp)
|
131
214
|
end
|
132
|
-
|
215
|
+
|
133
216
|
page.reload!
|
134
217
|
page.write!
|
135
|
-
|
218
|
+
|
136
219
|
# File should exist again, even though we just removed it since we re-wrote it.
|
137
220
|
if File.exists?(existing_tmp)
|
138
221
|
existing = File.join(destination, page.file_path)
|
139
222
|
|
140
|
-
if File.exists?(existing)
|
223
|
+
if File.exists?(existing)
|
141
224
|
log("Removing existing file [#{existing}]", :indent)
|
142
225
|
FileUtils.rm_rf(existing)
|
143
226
|
end
|
144
227
|
|
145
228
|
FileUtils.mkdir_p(File.dirname(existing))
|
146
229
|
FileUtils.cp(existing_tmp, existing)
|
147
|
-
|
230
|
+
|
148
231
|
log("File build complete.", :indent)
|
149
232
|
end
|
150
233
|
end
|
151
234
|
else
|
152
235
|
log("Cannot render file, it doesn't exist. [#{relative_file_path}]")
|
153
236
|
end
|
154
|
-
|
237
|
+
|
155
238
|
true
|
156
239
|
end
|
157
|
-
|
158
|
-
#
|
240
|
+
|
241
|
+
# The total number of all assets, layouts pages and posts in the source
|
242
|
+
# directory.
|
243
|
+
#
|
244
|
+
# @return [Integer]
|
159
245
|
def total_items
|
160
|
-
return 0 unless
|
161
|
-
@total_items ||=
|
246
|
+
return 0 unless site
|
247
|
+
@total_items ||= site.all_files.size
|
162
248
|
end
|
163
|
-
|
249
|
+
|
164
250
|
# Returns the time it took to run render! (in milliseconds)
|
251
|
+
#
|
252
|
+
# @return [Float] Milliseconds
|
165
253
|
def timer
|
166
|
-
return 0 unless @end_time and @start_time
|
167
|
-
(
|
254
|
+
return 0 unless @end_time and @start_time
|
255
|
+
(@end_time - @start_time).round
|
168
256
|
end
|
169
|
-
|
257
|
+
|
170
258
|
# The directory path of where to put the files while the site is being built.
|
171
259
|
#
|
172
260
|
# If this value is nil, no temporary directory is used and files are built
|
173
261
|
# directly in the normal destination folder.
|
262
|
+
#
|
263
|
+
# @return [String] Full file path
|
174
264
|
def tmp_destination
|
175
265
|
return @tmp_destination if @tmp_destination
|
176
|
-
|
266
|
+
|
177
267
|
result = ""
|
178
|
-
|
179
|
-
if
|
180
|
-
if
|
181
|
-
result = File.expand_path(
|
268
|
+
|
269
|
+
if options.has_key?(:tmp_destination)
|
270
|
+
if options[:tmp_destination]
|
271
|
+
result = File.expand_path(options[:tmp_destination])
|
182
272
|
end
|
183
273
|
else
|
184
274
|
result = File.join(cache_location, 'build-cache')
|
185
275
|
end
|
186
|
-
|
276
|
+
|
187
277
|
@tmp_destination = result
|
188
278
|
end
|
189
|
-
|
279
|
+
|
280
|
+
# Is this site using a temporary destination location?
|
281
|
+
#
|
282
|
+
# @return [Boolean]
|
190
283
|
def tmp_destination?
|
191
|
-
|
284
|
+
tmp_destination.to_s.size > 0
|
192
285
|
end
|
193
|
-
|
286
|
+
|
194
287
|
protected
|
195
288
|
# Allows process to continue if the source directory exists. If the source directory does not
|
196
289
|
# exist, raise a source does not exist error.
|
290
|
+
#
|
291
|
+
# @private
|
197
292
|
def check_source!
|
198
293
|
raise SourceNotFound unless directory_exists?(source)
|
199
294
|
end
|
200
|
-
|
295
|
+
|
201
296
|
# Copy all files from within the tmp/ build directory into the actual destination.
|
202
297
|
#
|
203
298
|
# Warning: This will overwrite any files already in the destination.
|
299
|
+
#
|
300
|
+
# @private
|
204
301
|
def copy_to_destination!
|
205
302
|
if items?
|
206
303
|
self.setup_destination!
|
207
|
-
|
304
|
+
|
208
305
|
if tmp_destination?
|
209
|
-
log("Copying content to destination directory")
|
306
|
+
log("Copying content to destination directory")
|
210
307
|
FileUtils.cp_r(Dir.glob("#{tmp_destination}**/*"), destination)
|
211
|
-
end
|
308
|
+
end
|
212
309
|
end
|
213
310
|
end
|
214
|
-
|
311
|
+
|
215
312
|
# Utility method for switching between ruby 1.8* and 1.9+
|
313
|
+
#
|
314
|
+
# @private
|
216
315
|
def directory_exists?(dir)
|
217
316
|
Dir.respond_to?(:exists?) ? Dir.exists?(dir) : File.directory?(dir)
|
218
317
|
end
|
219
|
-
|
318
|
+
|
220
319
|
# Loads the configuration options to use for rendering this site. By default, this information
|
221
320
|
# is loaded from a file located in config/plate.yml. If this file does not exist, no config
|
222
321
|
# data is loaded by default.
|
223
|
-
#
|
322
|
+
#
|
224
323
|
# You can specific additional options by passing them into the options block of this class:
|
225
324
|
#
|
226
325
|
# ## Custom Config File
|
227
|
-
#
|
228
|
-
# To load a different file, pass in the relative path of that file to the source root into the :config
|
326
|
+
#
|
327
|
+
# To load a different file, pass in the relative path of that file to the source root into the :config
|
229
328
|
# option:
|
230
329
|
#
|
231
330
|
# Builder.new(source, destination, :config => 'config/other-file.yml')
|
232
|
-
#
|
233
|
-
# On the command line when building a site, or creating a new post, you can specify the
|
331
|
+
#
|
332
|
+
# On the command line when building a site, or creating a new post, you can specify the
|
234
333
|
# custom config file as a command line option as well:
|
235
334
|
#
|
236
335
|
# plate build --config config/other-file.yml
|
237
|
-
#
|
336
|
+
#
|
337
|
+
# @return [Hash] Options hash. Also set to {#options}
|
238
338
|
def load_config_file!
|
239
339
|
config_file = 'config/plate.yml'
|
240
|
-
|
340
|
+
|
241
341
|
# Check for provided config options
|
242
342
|
if options.has_key?(:config)
|
243
343
|
# If config is false, just return without loading anything.
|
244
344
|
if options[:config] == false
|
245
|
-
log("Skipping config file load.")
|
246
|
-
config_file = false
|
345
|
+
log("Skipping config file load.")
|
346
|
+
config_file = false
|
247
347
|
# If something is provided for config set the config_file
|
248
348
|
else
|
249
349
|
config_file = options[:config]
|
250
350
|
end
|
251
351
|
end
|
252
|
-
|
352
|
+
|
253
353
|
if config_file
|
254
|
-
config_file_path = File.join(
|
354
|
+
config_file_path = File.join(source, config_file)
|
255
355
|
|
256
356
|
log("Checking for config file... [#{config_file_path}]")
|
257
357
|
|
@@ -267,72 +367,101 @@ module Plate
|
|
267
367
|
end
|
268
368
|
end
|
269
369
|
end
|
270
|
-
|
370
|
+
|
371
|
+
# If meta data was provided, add it to the site's meta data instance
|
372
|
+
if @options.has_key?(:meta) and Hash === @options[:meta]
|
373
|
+
@metadata = @options[:meta]
|
374
|
+
@metadata.symbolize_keys!
|
375
|
+
@options.delete(:meta)
|
376
|
+
end
|
377
|
+
|
378
|
+
# If a custom destination was provided in the config file, use it.
|
379
|
+
if @options.has_key?(:destination)
|
380
|
+
@destination = File.expand_path(@options[:destination])
|
381
|
+
end
|
382
|
+
|
271
383
|
# Make sure that the defaults are available.
|
272
384
|
@options.reverse_merge!({
|
273
385
|
:permalink => '/:category/:year/:month/:slug'
|
274
386
|
})
|
275
387
|
end
|
276
|
-
|
388
|
+
|
277
389
|
# Write to the log if enable_logging is enabled
|
390
|
+
#
|
391
|
+
# @private
|
278
392
|
def log(message, style = :arrow)
|
279
|
-
prefix = {
|
393
|
+
prefix = {
|
280
394
|
:arrow => ' -> ',
|
281
395
|
:indent => ' '
|
282
396
|
}[style] || style
|
283
|
-
|
397
|
+
|
284
398
|
puts "#{prefix}#{message}" if !!enable_logging
|
285
399
|
end
|
286
|
-
|
287
|
-
# Build out the site and store it in the destination directory
|
400
|
+
|
401
|
+
# Build out the site and store it in the destination directory.
|
402
|
+
#
|
403
|
+
# @private
|
288
404
|
def render_site!
|
289
405
|
if items?
|
290
406
|
log("Rendering site...")
|
291
|
-
|
407
|
+
|
292
408
|
paths = []
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
paths +=
|
297
|
-
paths +=
|
298
|
-
paths +=
|
299
|
-
|
409
|
+
|
410
|
+
site.run_callback(:before_render)
|
411
|
+
|
412
|
+
paths += site.assets.collect(&:write!)
|
413
|
+
paths += site.pages.collect(&:write!)
|
414
|
+
paths += site.posts.collect(&:write!)
|
415
|
+
|
300
416
|
@build_paths = paths
|
301
|
-
|
302
|
-
|
303
|
-
|
417
|
+
|
418
|
+
site.run_callback(:after_render)
|
419
|
+
|
304
420
|
log("Site rendered!", :indent)
|
305
421
|
else
|
306
422
|
log("No assets, posts or pages found. :(")
|
307
423
|
end
|
308
424
|
end
|
309
|
-
|
310
|
-
# Load any plugins
|
311
|
-
#
|
425
|
+
|
426
|
+
# Load any plugins in the ./lib folder and helper modules in the ./helpers folder.
|
427
|
+
#
|
428
|
+
# Any modules named with the format `SomethingHelper` will automatically be loaded
|
429
|
+
# into all views.
|
430
|
+
#
|
431
|
+
# @private
|
312
432
|
def require_plugins!
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
plugins = Dir.glob(File.join(source, "lib/**/*.rb"))
|
318
|
-
|
319
|
-
if plugins.length > 0
|
433
|
+
# For plugins, load all .rb files in the lib directory, regardless of name.
|
434
|
+
plugin_files = Dir.glob(File.join(source, 'lib/**/*.rb'))
|
435
|
+
|
436
|
+
if plugin_files.length > 0
|
320
437
|
log("Loading plugins...")
|
321
|
-
|
322
|
-
plugins.each do |file|
|
323
|
-
require file
|
324
438
|
|
439
|
+
plugin_files.each do |file|
|
440
|
+
Dsl.evaluate_plugin(file)
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
# For helpers, load all files that match `abc_helper.rb`, then check to make sure
|
445
|
+
# there is an appropriately named Module within that file.
|
446
|
+
@helpers = []
|
447
|
+
|
448
|
+
helper_files = Dir.glob(File.join(source, 'helpers/**/*.rb'))
|
449
|
+
matcher = /^#{Regexp.quote(File.join(source, 'helpers'))}\/?(.*).rb$/
|
450
|
+
|
451
|
+
if helper_files.length > 0
|
452
|
+
helper_files.each do |file|
|
325
453
|
underscore_name = file.sub(matcher, '\1')
|
326
454
|
|
327
|
-
# For helpers, make sure the module is defined, and add it to the helpers list
|
328
455
|
if underscore_name =~ /(.*?)_helper$/
|
456
|
+
require file
|
457
|
+
|
329
458
|
class_name = underscore_name.classify
|
330
459
|
|
331
460
|
if defined? class_name
|
332
461
|
log("Loaded helper [#{class_name}]", :indent)
|
333
462
|
|
334
|
-
klass = class_name.constantize
|
335
|
-
|
463
|
+
klass = class_name.constantize
|
464
|
+
@helpers << klass
|
336
465
|
|
337
466
|
View.send(:include, klass)
|
338
467
|
end
|
@@ -340,49 +469,56 @@ module Plate
|
|
340
469
|
end
|
341
470
|
end
|
342
471
|
end
|
343
|
-
|
344
|
-
# Clear out the destination directory, if it exists. Leave the root of the
|
472
|
+
|
473
|
+
# Clear out the destination directory, if it exists. Leave the root of the
|
345
474
|
# destination itself, but clear any files within it.
|
475
|
+
#
|
476
|
+
# @private
|
346
477
|
def setup_destination!
|
347
478
|
if directory_exists?(destination)
|
348
479
|
log("Clearing destination directory [#{destination}]")
|
349
|
-
|
480
|
+
|
350
481
|
FileUtils.rm_r(Dir.glob("#{destination}**/*"), :force => true)
|
351
482
|
elsif items?
|
352
483
|
log("Creating destination directory [#{destination}]")
|
353
|
-
|
354
|
-
FileUtils.mkdir_p(destination)
|
484
|
+
|
485
|
+
FileUtils.mkdir_p(destination)
|
355
486
|
end
|
356
487
|
end
|
357
|
-
|
488
|
+
|
358
489
|
# Setup the Site instance and prepare it for loading
|
490
|
+
#
|
491
|
+
# @private
|
359
492
|
def setup_site!
|
360
493
|
log("Setting up site instance")
|
361
|
-
|
362
|
-
self.site = Site.new(source, destination, options)
|
494
|
+
|
495
|
+
self.site = Site.new(source, destination, options)
|
363
496
|
self.site.logger = self
|
364
497
|
self.site.cache_location = self.cache_location
|
365
|
-
|
498
|
+
self.site.metadata = self.metadata
|
499
|
+
|
366
500
|
log("Site data loaded from source")
|
367
501
|
end
|
368
|
-
|
369
|
-
# Create a temporary folder to build everything in. Once the build was successful,
|
502
|
+
|
503
|
+
# Create a temporary folder to build everything in. Once the build was successful,
|
370
504
|
# all files will then be placed into the actual destination.
|
505
|
+
#
|
506
|
+
# @private
|
371
507
|
def setup_tmp_directory!
|
372
508
|
return unless tmp_destination?
|
373
|
-
|
509
|
+
|
374
510
|
log("Setting up tmp build directory [#{tmp_destination}]")
|
375
|
-
|
511
|
+
|
376
512
|
# Clear out any existing tmp folder contents
|
377
513
|
if directory_exists?(tmp_destination)
|
378
514
|
log("Clearing existing tmp directory content")
|
379
|
-
|
515
|
+
|
380
516
|
FileUtils.rm_rf(tmp_destination)
|
381
517
|
end
|
382
|
-
|
518
|
+
|
383
519
|
FileUtils.mkdir_p(tmp_destination)
|
384
|
-
|
385
|
-
|
520
|
+
|
521
|
+
site.build_destination = tmp_destination
|
386
522
|
end
|
387
523
|
end
|
388
524
|
end
|