massimo 0.3.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.document +5 -0
  2. data/.gitignore +25 -0
  3. data/LICENSE +20 -0
  4. data/README.md +108 -0
  5. data/Rakefile +62 -0
  6. data/VERSION +1 -0
  7. data/bin/massimo +6 -0
  8. data/lib/massimo/command.rb +242 -0
  9. data/lib/massimo/helpers.rb +26 -0
  10. data/lib/massimo/javascript.rb +38 -0
  11. data/lib/massimo/page.rb +113 -0
  12. data/lib/massimo/resource.rb +52 -0
  13. data/lib/massimo/site.rb +186 -0
  14. data/lib/massimo/stylesheet.rb +46 -0
  15. data/lib/massimo/templates.rb +20 -0
  16. data/lib/massimo/view.rb +30 -0
  17. data/lib/massimo.rb +38 -0
  18. data/massimo.gemspec +130 -0
  19. data/test/assertions.rb +8 -0
  20. data/test/helper.rb +54 -0
  21. data/test/source/config.yml +4 -0
  22. data/test/source/helpers/test_helper.rb +5 -0
  23. data/test/source/javascripts/_plugin.js +1 -0
  24. data/test/source/javascripts/application.js +3 -0
  25. data/test/source/javascripts/lib.js +1 -0
  26. data/test/source/lib/site.rb +5 -0
  27. data/test/source/pages/_skipped_page.haml +0 -0
  28. data/test/source/pages/about_us.erb +5 -0
  29. data/test/source/pages/erb.erb +5 -0
  30. data/test/source/pages/erb_with_layout.erb +4 -0
  31. data/test/source/pages/feed.haml +6 -0
  32. data/test/source/pages/haml.haml +5 -0
  33. data/test/source/pages/html.html +4 -0
  34. data/test/source/pages/index.erb +4 -0
  35. data/test/source/pages/markdown.markdown +5 -0
  36. data/test/source/pages/posts/first-post.haml +1 -0
  37. data/test/source/pages/with_extension.haml +4 -0
  38. data/test/source/pages/with_meta_data.haml +7 -0
  39. data/test/source/pages/with_title.haml +4 -0
  40. data/test/source/pages/with_url.haml +4 -0
  41. data/test/source/pages/without_extension.haml +0 -0
  42. data/test/source/pages/without_meta_data.haml +1 -0
  43. data/test/source/pages/without_title.haml +1 -0
  44. data/test/source/pages/without_url.haml +1 -0
  45. data/test/source/stylesheets/_base.sass +2 -0
  46. data/test/source/stylesheets/application.sass +4 -0
  47. data/test/source/stylesheets/basic.css +3 -0
  48. data/test/source/stylesheets/less_file.less +5 -0
  49. data/test/source/views/layouts/application.haml +2 -0
  50. data/test/source/views/with_helper.haml +1 -0
  51. data/test/source/views/with_locals.haml +1 -0
  52. data/test/source/views/without_locals.haml +1 -0
  53. data/test/test_javascript.rb +30 -0
  54. data/test/test_page.rb +136 -0
  55. data/test/test_resource.rb +27 -0
  56. data/test/test_site.rb +126 -0
  57. data/test/test_stylesheet.rb +40 -0
  58. data/test/test_view.rb +46 -0
  59. metadata +191 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+ .eprj
8
+
9
+ ## EMACS
10
+ *~
11
+ \#*
12
+ .\#*
13
+
14
+ ## VIM
15
+ *.swp
16
+
17
+ ## PROJECT::GENERAL
18
+ coverage
19
+ rdoc
20
+ pkg
21
+ doc
22
+ .yardoc
23
+ .sass-cache
24
+
25
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Peter Browne
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # Massimo
2
+
3
+ Massimo is a full static website generator. While making [Rails](http://rubyonrails.org/) websites I became dependent on [Haml](http://haml-lang.com/), [Sass](http://sass-lang.com/), and other useful gems. I wanted to use that workflow to rapidly make simple, static websites. Massimo's code is inspired by other website generators like [Jekyll](http://github.com/mojombo/jekyll) and [Webby](http://webby.rubyforge.org/). It's features include:
4
+
5
+ * It renders templates and views using [Tilt](http://github.com/rtomayko/tilt)
6
+ * It uses familiar helper methods from [Sinatra::More](http://github.com/nesquena/sinatra_more)
7
+ * It supports custom helper methods like [Rails](http://rubyonrails.org/) and [Sinatra](http://www.sinatrarb.com/)
8
+ * It concats javascripts using [Sprockets](http://getsprockets.org/)
9
+ and then minifies them using [JSMin](http://github.com/rgrove/jsmin)
10
+ * It renders stylesheets using either [Sass](http://sass-lang.com/) or [Less](http://lesscss.org/)
11
+
12
+
13
+ ## Basic Usage
14
+
15
+ 1. Setup the structure of the Site
16
+ 2. Create some pages
17
+ 3. Run you Site locally to see how it looks
18
+ 4. Deploy your Site
19
+
20
+ ### Structure
21
+
22
+ A basic Massimo Site looks something like this, though each directory's path can be customized:
23
+
24
+ .
25
+ |-- config.yml
26
+ |
27
+ |-- helpers
28
+ | `-- my_helpers.rb
29
+ |
30
+ |-- javascripts
31
+ | |-- _plugin.js
32
+ | `-- application.js
33
+ |
34
+ |-- lib
35
+ | `-- post.rb
36
+ |
37
+ |-- pages
38
+ | |-- index.haml
39
+ | |-- contact.haml
40
+ | `-- about-us.haml
41
+ |
42
+ |-- stylesheets
43
+ | |-- _base.sass
44
+ | `-- application.sass
45
+ |
46
+ |-- views
47
+ | |-- partial.haml
48
+ | `-- layouts
49
+ | `-- applcation.haml
50
+ |
51
+ `-- public
52
+
53
+ #### config.yml
54
+
55
+ This where you setup the options for the Site.
56
+
57
+ #### helpers
58
+
59
+ This is where you put helper modules (like Rails). This modules will automatically be available in your pages and views.
60
+
61
+ #### javascripts
62
+
63
+ This is where you put the working copies of your javascripts. They will be concatenated, minified, and moved to your output directory when the site is processed.
64
+
65
+ #### lib
66
+
67
+ This is where you put additional libraries. You can customize the default Massimo classes or add your own here. This is where you would add additional Tilt Templates.
68
+
69
+ #### pages
70
+
71
+ These are the actual pages (content) of your site. Anything here that is registered by a Tilt Template will be transformed into HTML and moved to the appropriate place in the output directory.
72
+
73
+ #### stylesheets
74
+
75
+ This is where you put the working copies of your stylesheets. If they are Sass or Less documents, they will be transformed into CSS documents then moved to your output directory.
76
+
77
+ #### views
78
+
79
+ This is where you put partials and layouts (like Rails). You can render partials from your pages by calling `render("partial_name")`.
80
+
81
+ ### Running Massimo
82
+
83
+ Usually this is done through the massimo executable, which is installed with the gem. In order to get a server up and running with your Massimo site, run `massimo --server` and then browse to http://localhost:1984/. Or you could simply run `massimo --watch` to watch for changes and regenerate the site.
84
+
85
+
86
+ ## YAML Front Matter
87
+
88
+ Pages can contain YAML front matter blocks for either predefined configuration options or custom variables. The front matter must be the first thing in the file and takes the form of:
89
+
90
+ ---
91
+ title: Using YAML Front Matter
92
+ layout: false
93
+ ---
94
+
95
+ ## Note on Patches/Pull Requests
96
+
97
+ * Fork the project.
98
+ * Make your feature addition or bug fix.
99
+ * Add tests for it. This is important so I don't break it in a
100
+ future version unintentionally.
101
+ * Commit, do not mess with Rakefile, VERSION, or history.
102
+ (if you want to have your own version, that is fine but
103
+ bump version in a commit by itself I can ignore when I pull)
104
+ * Send me a pull request. Bonus points for topic branches.
105
+
106
+ ## Copyright
107
+
108
+ Copyright (c) 2009 [Peter Browne](http://peterbrowne.net). See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,62 @@
1
+ require "rubygems"
2
+ require "rake"
3
+
4
+ begin
5
+ require "jeweler"
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "massimo"
8
+ gem.summary = %{Massimo is a static website builder.}
9
+ gem.description = %{Massimo builds HTML, Javascript, and CSS Files from your source.}
10
+ gem.email = "peter@peterbrowne.net"
11
+ gem.homepage = "http://github.com/peterbrowne/massimo"
12
+ gem.authors = [ "Peter Browne" ]
13
+ gem.add_development_dependency "shoulda", ">= 2.10.2"
14
+ gem.add_development_dependency "yard", ">= 0.5.2"
15
+ gem.add_dependency "activesupport", ">= 2.3.5"
16
+ gem.add_dependency "sinatra_more", ">= 0.3.26"
17
+ gem.add_dependency "directory_watcher", ">= 1.3.1"
18
+ gem.add_dependency "sprockets", ">= 1.0.2"
19
+ gem.add_dependency "jsmin", ">= 1.0.1"
20
+ end
21
+ Jeweler::GemcutterTasks.new
22
+ rescue LoadError
23
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
24
+ end
25
+
26
+ require "rake/testtask"
27
+ Rake::TestTask.new(:test) do |test|
28
+ test.libs << "lib" << "test"
29
+ test.pattern = "test/**/test_*.rb"
30
+ test.verbose = true
31
+ end
32
+
33
+ begin
34
+ require "rcov/rcovtask"
35
+ Rcov::RcovTask.new do |test|
36
+ test.libs << "test"
37
+ test.pattern = "test/**/test_*.rb"
38
+ test.verbose = true
39
+ end
40
+ rescue LoadError
41
+ task :rcov do
42
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
43
+ end
44
+ end
45
+
46
+ task :test => :check_dependencies
47
+
48
+ task :default => :test
49
+
50
+ desc "Open an irb session preloaded with this library"
51
+ task :console do
52
+ sh "irb -rubygems -I lib -r massimo.rb"
53
+ end
54
+
55
+ begin
56
+ require "yard"
57
+ YARD::Rake::YardocTask.new
58
+ rescue LoadError
59
+ task :yardoc do
60
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
61
+ end
62
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.9
data/bin/massimo ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), *%w{.. lib massimo}))
4
+ require File.expand_path(File.join(File.dirname(__FILE__), *%w{.. lib massimo command}))
5
+
6
+ exit Massimo::Command.new(ARGV).run!
@@ -0,0 +1,242 @@
1
+ require "optparse"
2
+ require "active_support"
3
+ require "yaml"
4
+ begin require "growl"; rescue ::LoadError; end
5
+
6
+ module Massimo
7
+ class Command
8
+ attr_accessor :args, :options, :site, :source, :output
9
+
10
+ # Default options. Overriden by values in config.yml or command-line opts.
11
+ DEFAULT_OPTIONS = {
12
+ :config_path => ::File.join(".", "config.yml")
13
+ }.freeze
14
+
15
+ #
16
+ def initialize(args)
17
+ # Parse the command line arguments
18
+ self.args = args
19
+ self.options = DEFAULT_OPTIONS.dup
20
+ self.parse!
21
+
22
+ # Load the options from the config file
23
+ config = ::YAML.load_file(self.options[:config_path]) if ::File.exist?(self.options[:config_path])
24
+ self.options.merge!(config.symbolize_keys) if config.is_a?(::Hash)
25
+
26
+ # Initialize the Site
27
+ self.site = ::Massimo::Site(self.options)
28
+ self.options = self.site.options
29
+ self.source = self.options[:source]
30
+ self.output = self.options[:output]
31
+
32
+ # Setup Backtrace Cleaner
33
+ @cleaner = ::ActiveSupport::BacktraceCleaner.new
34
+ @cleaner.add_silencer { |line| line =~ /^(\/|\\)/ } # Remove full File path traces
35
+ end
36
+
37
+ # Run the script, based on the command line options.
38
+ def run!
39
+ if generate?
40
+ generate_layout!
41
+ elsif watch?
42
+ watch_source!
43
+ else
44
+ process_site!
45
+ end
46
+ run_server! if server?
47
+ return 0
48
+ rescue ::Interrupt
49
+ message "Massimo is done watching you.", :newline => true
50
+ return 0
51
+ rescue ::Exception => e
52
+ report_error(e)
53
+ return 1
54
+ end
55
+
56
+ protected
57
+
58
+ # Generate the default layout of the site.
59
+ def generate_layout!
60
+ require "fileutils"
61
+ message "Massimo is generating the default site layout"
62
+ [ site.source_dir, site.all_source_dirs, site.output_dir ].flatten.each do |dir|
63
+ full_dir = ::File.expand_path(dir)
64
+ if ::File.exists?(full_dir)
65
+ puts indent_body("exists: #{full_dir}")
66
+ else
67
+ ::FileUtils.mkdir_p(full_dir)
68
+ puts indent_body("created: #{full_dir}")
69
+ end
70
+ end
71
+ end
72
+
73
+ # Watch the source for changes.
74
+ def watch_source!
75
+ require "directory_watcher"
76
+
77
+ message %{Massimo is watching "#{source}" for changes. Press Ctrl-C to Stop.}
78
+
79
+ watcher = ::DirectoryWatcher.new(
80
+ ".",
81
+ :interval => 1,
82
+ :glob => site.all_source_dirs.collect { |dir| ::File.join(dir, *%w{** *}) }
83
+ )
84
+
85
+ watcher.add_observer do |*args|
86
+ begin
87
+ site.process!
88
+ time = ::Time.now.strftime("%H:%M")
89
+ change = args.size == 1 ? "1 file" : "#{args.size} files"
90
+ message "Massimo has rebuilt your site. #{change} changed. (#{time})"
91
+ rescue ::Exception => e
92
+ report_error(e)
93
+ end
94
+ end
95
+
96
+ watcher.start
97
+
98
+ unless server?
99
+ loop { sleep 1000 }
100
+ end
101
+ end
102
+
103
+ # Process the site.
104
+ def process_site!
105
+ site.process!
106
+ message %{Massimo has built your site in "#{site.options[:output]}"}
107
+ end
108
+
109
+ #
110
+ def run_server!
111
+ require "webrick"
112
+
113
+ # Make sure the output dir exists
114
+ ::FileUtils.mkdir_p(output)
115
+
116
+ server = ::WEBrick::HTTPServer.new(
117
+ :Port => options[:server_port],
118
+ :DocumentRoot => output
119
+ )
120
+
121
+ trap(:INT) do
122
+ server.shutdown
123
+ message "Massimo is shutting down the server.", :newline => true
124
+ return 0
125
+ end
126
+
127
+ server.start
128
+ message "Massimo is serving up your site at http://localhost:#{options[:server_port]}/"
129
+ end
130
+
131
+ # Determine if we should watch the source directory for changes.
132
+ def watch?
133
+ options[:watch] == true
134
+ end
135
+
136
+ # Determine if we should generate the default layout of the site.
137
+ def generate?
138
+ options[:generate] == true
139
+ end
140
+
141
+ # Determine if the server should be started.
142
+ def server?
143
+ options[:server] == true
144
+ end
145
+
146
+ #
147
+ def message(string, options = {})
148
+ options.reverse_merge!(:growl => true)
149
+ puts "\n" if options[:newline]
150
+ puts "== #{string}"
151
+ ::Growl.notify(string, :title => "Massimo") if options[:growl] && defined?(::Growl)
152
+ end
153
+
154
+ # Report the given error. This could eventually log the backtrace.
155
+ def report_error(error = nil)
156
+ error ||= $!
157
+
158
+ # Show full backtrace if verbose
159
+ backtrace = if options[:verbose]
160
+ error.backtrace
161
+ else
162
+ @cleaner.clean(error.backtrace)
163
+ end
164
+
165
+ # show the message
166
+ message "Massimo Error:", :newline => true, :growl => false
167
+ puts indent_body(error.message)
168
+ puts indent_body(backtrace)
169
+ puts "\n"
170
+
171
+ # Format the message differently for growl
172
+ ::Growl.notify(error.message, :title => "Massimo Error") if defined?(::Growl)
173
+ end
174
+
175
+ # Returns the string with each line indented.
176
+ def indent_body(string)
177
+ string.collect { |line| " #{line}" }
178
+ end
179
+
180
+ # Parse the options
181
+ def parse!
182
+ opts = ::OptionParser.new do |opts|
183
+ opts.banner = <<-HELP
184
+ Massimo is a static website builder.
185
+
186
+ Basic Command Line Usage:
187
+ massimo # . -> ./public
188
+ massimo <path to write generated site> # . -> <path>
189
+ massimo <path to source> <path to write generated site> # <path> -> <path>
190
+
191
+ Configuration is read from "<source>/config.yml" but can be overriden
192
+ using the following options:
193
+
194
+ HELP
195
+
196
+ opts.on("--config [PATH]", "The path to the config file.") do |path|
197
+ options[:config_path] = path
198
+ end
199
+
200
+ opts.on("--generate", "Generate the default layout of the site.") do
201
+ options[:generate] = true
202
+ end
203
+
204
+ opts.on("--watch", "Auto-regenerate the site as files are changed.") do
205
+ options[:watch] = true
206
+ end
207
+
208
+ opts.on("--server", "Start web server with default port.") do |port|
209
+ options[:server] = true
210
+ end
211
+
212
+ opts.on("--port [PORT]", "Select the port to start the web server on. Defaults to 1984") do |port|
213
+ options[:server_port] = port
214
+ end
215
+
216
+ opts.on("--verbose", "-v", "Show full backtrace on errors. Defaults to false.") do
217
+ options[:verbose] = true
218
+ end
219
+
220
+ opts.on("--version", "-V", "Display current version") do
221
+ puts "Massimo #{::Massimo::VERSION}"
222
+ exit 0
223
+ end
224
+ end
225
+ opts.parse!
226
+
227
+ # Get source and destintation from command line
228
+ case args.size
229
+ when 0
230
+ when 1
231
+ options[:source] = args[0]
232
+ when 2
233
+ options[:source] = args[0]
234
+ options[:output] = args[1]
235
+ else
236
+ puts %{Invalid options. Run "massimo --help" for assistance.}
237
+ exit 1
238
+ end
239
+ end
240
+
241
+ end
242
+ end
@@ -0,0 +1,26 @@
1
+ module Massimo
2
+ class Helpers
3
+ if defined? ::SinatraMore
4
+ include ::SinatraMore::OutputHelpers
5
+ include ::SinatraMore::TagHelpers
6
+ include ::SinatraMore::AssetTagHelpers
7
+ include ::SinatraMore::FormHelpers
8
+ include ::SinatraMore::FormatHelpers
9
+ end
10
+
11
+ #
12
+ def initialize(modules = nil)
13
+ self.extend(*modules) unless modules.nil? || modules.empty?
14
+ end
15
+
16
+ # Gets the site instance
17
+ def site
18
+ ::Massimo::Site()
19
+ end
20
+
21
+ #
22
+ def render(name, locals = {}, &block)
23
+ self.site.render_view(name, locals, &block)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,38 @@
1
+ module Massimo
2
+ class Javascript < Resource
3
+ # Concat the Javascript using Sprockets, then minify using JSmin
4
+ def render
5
+ secretary = ::Sprockets::Secretary.new(
6
+ :assert_root => self.site.output_dir,
7
+ :source_files => [ @source_path.to_s ]
8
+ )
9
+ # install assets if necessary
10
+ secretary.install_assets
11
+
12
+ if self.site.production? or self.site.options[:minify]
13
+ # minify the concatenated javascript
14
+ ::JSMin.minify(secretary.concatenation.to_s)
15
+ else
16
+ secretary.concatenation.to_s
17
+ end
18
+ end
19
+
20
+ # Writes the rendered js to the output file.
21
+ def process!
22
+ # Make the full path to the directory of the output file
23
+ ::FileUtils.mkdir_p(self.output_path.dirname)
24
+ # write the filtered data to the output file
25
+ self.output_path.open("w") do |file|
26
+ file.write self.render
27
+ end
28
+ end
29
+
30
+ protected
31
+
32
+ # Determine the output file path
33
+ def output_path
34
+ @output_path ||= ::Pathname.new(@source_path.to_s.
35
+ sub(self.site.source_dir, self.site.output_dir)) # move to output dir
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,113 @@
1
+ module Massimo
2
+ class Page < View
3
+ META_SEP = %r/\A---\s*(?:\r\n|\n)?\z/ # :nodoc:
4
+
5
+ # Creates a new page associated with the given file path.
6
+ def initialize(source_path)
7
+ @source_path = ::Pathname.new(source_path)
8
+ @meta_data = {
9
+ :title => @source_path.basename.to_s.gsub(@source_path.extname, "").titleize,
10
+ :extension => ".html",
11
+ :url => @source_path.to_s.gsub(self.site.pages_dir, "").gsub(@source_path.extname, "").dasherize,
12
+ :layout => "application"
13
+ }
14
+ # read and parse the source file
15
+ self.read_source!
16
+ end
17
+
18
+ # Override render to wrap the result in the layout
19
+ def render(with_layout = true)
20
+ if with_layout && layout = self.find_layout
21
+ layout.render(:page => self) { self.render(false) }
22
+ else
23
+ super()
24
+ end
25
+ end
26
+
27
+ # Writes the filtered data to the output file.
28
+ def process!
29
+ refresh_layout
30
+ path = self.output_path
31
+ # Make the full path to the directory of the output file
32
+ ::FileUtils.mkdir_p(path.dirname)
33
+ # write the filtered data to the output file
34
+ path.open("w") do |file|
35
+ file.write self.render(self.layout?)
36
+ end
37
+ end
38
+
39
+ # Override to_s so that the layout can include the page with <%= page %>
40
+ def to_s
41
+ self.render(false)
42
+ end
43
+
44
+ protected
45
+
46
+ # Reads the source page file, and populates the meta_data and
47
+ # body attributes.
48
+ def read_source!
49
+ # read the source file and setup some values for the loop
50
+ source = super()
51
+ @line = nil
52
+ front_matter = false
53
+ meta_data = ""
54
+ body = ""
55
+
56
+ # Loop through source to get meta data
57
+ # and the correct line number for the body
58
+ source.each_with_index do |line, line_num|
59
+ if line =~ META_SEP
60
+ front_matter = !front_matter
61
+ else
62
+ if front_matter
63
+ meta_data << line
64
+ else
65
+ @line ||= line_num
66
+ body << line
67
+ end
68
+ end
69
+ end
70
+
71
+ # finally get the meta_data as a hash and set the body
72
+ meta_data = ::YAML.load(meta_data)
73
+ @meta_data.merge!(meta_data.symbolize_keys) if meta_data
74
+ @body = body
75
+ end
76
+
77
+ # Determine the output file path
78
+ def output_path
79
+ path = self.site.output_dir(self.url)
80
+ path << if index? or not html?
81
+ self.extension unless path.match(/#{self.extension}$/)
82
+ else
83
+ "/index.html" unless path.match(/\/index\.html$/)
84
+ end
85
+ ::Pathname.new(path)
86
+ end
87
+
88
+ # Determines if this is an index page
89
+ def index?
90
+ @source_path.basename.to_s =~ /^index/
91
+ end
92
+
93
+ # Determines if this an HTML page.
94
+ def html?
95
+ self.extension =~ /(html|php)$/
96
+ end
97
+
98
+ # The next time `find_layout` is called, the layout will be reloaded.
99
+ def refresh_layout
100
+ @layout_view = nil
101
+ end
102
+
103
+ # Determines if there's a layout associated with this page.
104
+ def layout?
105
+ self.layout != false && !self.find_layout.nil?
106
+ end
107
+
108
+ # Finds the Layout View if it exists
109
+ def find_layout
110
+ @layout_view ||= self.site.find_view("layouts/#{self.layout}") unless self.layout == false
111
+ end
112
+ end
113
+ end