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.
@@ -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