plate 0.5.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  [![Build Status](https://secure.travis-ci.org/jdtornow/plate.png)](http://travis-ci.org/jdtornow/plate) [![Dependency Status](https://gemnasium.com/jdtornow/plate.png?travis)](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 will be loaded automatically.
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
- Plate::Site.register_callback :after_render do |site|
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 `lib/sample_helper.rb`
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(ruby -I"lib:test" test/test_builder.rb -n /sample/)
19
+ sh %q(turn -I"lib:test" test/test_builder.rb -n /sample/)
21
20
  end
22
21
  end
23
22
 
24
- YARD::Rake::YardocTask.new do |t|
25
- end
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
@@ -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
@@ -1,50 +1,113 @@
1
1
  module Plate
2
- # An asset is a CoffeeScript or Sass file that needs to be compiled before writing
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
- def file_path
18
- "#{directory}/#{file_name}"
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
- # Same as page, but no layout applied
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
- result = File.read(file)
30
-
31
- self.engines.each do |engine|
32
- template = engine.new() { result }
33
- result = template.render(self, :site => self.site)
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 = result
86
+
87
+ @rendered_content
37
88
  end
38
-
39
- # Write this page to the destination. For static files this just results
40
- # in copying the file over to the destination
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
- File.open(path, 'w') do |f|
46
- f.write(self.rendered_content)
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
@@ -1,257 +1,357 @@
1
1
  require 'yaml'
2
2
  require 'digest'
3
3
 
4
- module Plate
5
- # Used by the command line tool to generate a site in the given directory.
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
- attr_accessor :source, :destination, :options, :site, :enable_logging, :helpers
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
- @options = Hash === options ? options.clone : {}
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, also resets any variables for the caching and
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
- # Returns the options for this site.
74
+
75
+ # Loads the site, if not already loaded and returns the options listed.
76
+ #
77
+ # @return [Hash]
37
78
  def config
38
- self.load!
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
- self.require_plugins!
58
- self.load_config_file!
59
- self.setup_site!
60
- self.setup_tmp_directory!
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
- self.site.reload!
78
- self.render_site!
79
- self.copy_to_destination!
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
- # Called to start the rendering of the site based on the provided, source, destination and config options.
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
- log("Building full site...")
96
-
97
- self.load!
98
- self.render_site!
99
- self.copy_to_destination!
100
-
101
- @end_time = Time.now
102
-
103
- log("Site build completed in #{timer} seconds")
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
- self.load!
110
-
111
- page = self.site.find(relative_file_path)
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
- # if the file is a layout, rebuild all pages using it
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
- self.site.find_by_layout(page.relative_file).each do |layout_page|
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
- # Remove tmp file
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
- # Total number of all assets, posts and pages.
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 self.site
161
- @total_items ||= self.site.all_files.size
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
- ((@end_time - @start_time)).round
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 self.options.has_key?(:tmp_destination)
180
- if self.options[:tmp_destination]
181
- result = File.expand_path(self.options[:tmp_destination])
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
- self.tmp_destination.to_s.size > 0
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(self.source, config_file)
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
- self.site.run_callback(:before_render)
295
-
296
- paths += self.site.assets.collect(&:write!)
297
- paths += self.site.pages.collect(&:write!)
298
- paths += self.site.posts.collect(&:write!)
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
- self.site.run_callback(:after_render)
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 and helpers in the ./lib folder. Any modules named with the
311
- # format SomethingHelper will automatically be loaded into all views.
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
- self.helpers = []
314
-
315
- matcher = /^#{Regexp.quote(File.join(source, 'lib'))}\/?(.*).rb$/
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
- self.helpers << klass
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
- self.site.build_destination = tmp_destination
520
+
521
+ site.build_destination = tmp_destination
386
522
  end
387
523
  end
388
524
  end