diff --git a/CHANGELOG b/CHANGELOG index a87c6452..0a0e6a61 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,4 +2,5 @@ ===== - Combine views/ and public/ into a single source/ folder. -- Support YAML front-matter \ No newline at end of file +- Support YAML front-matter +- Blog-aware \ No newline at end of file diff --git a/bin/mm-server b/bin/mm-server index a4a8d391..00f97555 100755 --- a/bin/mm-server +++ b/bin/mm-server @@ -66,20 +66,8 @@ if File.exists?(old_views) || File.exists?(old_public) end Middleman::Server.root = @current_path -app = Middleman::Server.new +options[:app] = Middleman::Server.new +# options[:server] = 'webrick' -# TODO: Remove this -require 'rubygems' - -unless defined?(JRUBY_VERSION) - require 'thin' - handler = Rack::Handler::Thin -else - require 'kirk' - require 'rack/handler/kirk' - handler = Rack::Handler::Kirk -end - -handler.run app, options do |inst| - puts "== The Middleman is standing watch on port #{options[:Port]}" -end +puts "== The Middleman is standing watch on port #{options[:Port]}" +Rack::Server.new(options).start diff --git a/lib/middleman/features.rb b/lib/middleman/features.rb index b448c6f0..a0f205b5 100644 --- a/lib/middleman/features.rb +++ b/lib/middleman/features.rb @@ -71,6 +71,12 @@ module Middleman::Features # to dynamic requests. autoload :Data, "middleman/features/data" + # Parse YAML metadata from templates + autoload :FrontMatter, "middleman/features/front_matter" + + # Treat project as a blog + autoload :Blog, "middleman/features/blog" + # Proxy web services requests in dev mode only # autoload :Proxy, "middleman/features/proxy" diff --git a/lib/middleman/features/blog.rb b/lib/middleman/features/blog.rb new file mode 100644 index 00000000..0fde10bf --- /dev/null +++ b/lib/middleman/features/blog.rb @@ -0,0 +1,121 @@ +require "rdiscount" + +module Middleman + module Features + module Blog + class << self + def registered(app) + # Depend on FrontMatter + app.activate Middleman::Features::FrontMatter + + # Include helpers + app.helpers Middleman::Features::Blog::Helpers + + app.after_feature_init do + if !app.settings.respond_to? :blog_permalink + app.set :blog_permalink, "/:year/:month/:day/:title.html" + end + + if !app.settings.respond_to? :blog_layout + app.set :blog_layout, "layout" + end + + if !app.settings.respond_to? :blog_summary_separator + app.set :blog_summary_separator, /READMORE/ + end + + if !app.settings.respond_to? :blog_summary_length + app.set :blog_summary_length, 250 + end + + if !app.settings.respond_to? :blog_layout_engine + app.set :blog_layout_engine, "erb" + end + + if !app.settings.respond_to? :blog_index_template + app.set :blog_index_template, "index_template" + end + + if !app.settings.respond_to? :blog_article_template + app.set :blog_article_template, "article_template" + end + + $stderr.puts "== Blog: #{app.settings.blog_permalink}" + + articles_glob = File.join(app.views, app.settings.blog_permalink.gsub(/(:\w+)/, "*") + ".*") + + articles = Dir[articles_glob].map do |article| + template_content = File.read(article) + data, content = parse_front_matter(template_content) + data["date"] = Date.parse(data["date"]) + + yaml_regex = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m + data["raw"] = template_content.split(yaml_regex).last + data["url"] = article.gsub(app.views, "").split(".html").first + ".html" + + all_content = Tilt.new(article).render + data["body"] = all_content.gsub!(app.settings.blog_summary_separator, "") + + sum = if data["raw"] =~ app.settings.blog_summary_separator + data["raw"].split(app.settings.blog_summary_separator).first + else data["raw"].match(/(.{1,#{app.settings.blog_summary_length}}.*?)(\n|\Z)/m).to_s + end + + engine = RDiscount.new(sum) + data["summary"] = engine.to_html + data + end.sort { |a, b| b["date"] <=> a["date"] } + + app.data_content("blog", { :articles => articles }) + + app.get(app.settings.blog_permalink) do + options = {} + options[:layout] = settings.blog_layout + options[:layout_engine] = settings.blog_layout_engine + + extensionless_path, template_engine = resolve_template(request.path) + + full_file_path = "#{extensionless_path}.#{template_engine}" + system_path = File.join(settings.views, full_file_path) + data, content = self.class.parse_front_matter(File.read(system_path)) + + # Forward remaining data to helpers + self.class.data_content("page", data) + + output = render(request.path, options) + + # No need for separator on permalink page + output.gsub!(settings.blog_summary_separator, "") + + status 200 + output + end + end + + end + alias :included :registered + end + + module Helpers + def is_blog_article? + !current_article_title.blank? + end + + def blog_title + end + + def current_article_date + DateTime.parse(current_article_metadata.date) + end + + def current_article_title + current_article_metadata.title + end + + def current_article_metadata + data.page + end + end + end + end +end \ No newline at end of file diff --git a/lib/middleman/features/front_matter.rb b/lib/middleman/features/front_matter.rb new file mode 100644 index 00000000..0db76884 --- /dev/null +++ b/lib/middleman/features/front_matter.rb @@ -0,0 +1,69 @@ +require "yaml" +require "tilt" + +module Middleman::Features::FrontMatter + class << self + def registered(app) + app.extend ClassMethods + + ::Tilt::register MarukuTemplate, 'markdown', 'mkd', 'md' + ::Tilt::register KramdownTemplate, 'markdown', 'mkd', 'md' + ::Tilt::register BlueClothTemplate, 'markdown', 'mkd', 'md' + ::Tilt::register RedcarpetTemplate, 'markdown', 'mkd', 'md' + ::Tilt::register RDiscountTemplate, 'markdown', 'mkd', 'md' + ::Tilt::register RedClothTemplate, 'textile' + end + alias :included :registered + end + + module ClassMethods + def parse_front_matter(content) + yaml_regex = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m + if content =~ yaml_regex + begin + data = YAML.load($1) + rescue => e + puts "YAML Exception: #{e.message}" + end + + content = content.split(yaml_regex).last + end + + data ||= {} + [data, content] + end + end + + module YamlAware + def prepare + options, @data = Middleman::Server.parse_front_matter(@data) + super + end + end + + # MARKDOWN + class MarukuTemplate < ::Tilt::MarukuTemplate + include Middleman::Features::FrontMatter::YamlAware + end + + class KramdownTemplate < ::Tilt::KramdownTemplate + include Middleman::Features::FrontMatter::YamlAware + end + + class BlueClothTemplate < ::Tilt::BlueClothTemplate + include Middleman::Features::FrontMatter::YamlAware + end + + class RedcarpetTemplate < ::Tilt::RedcarpetTemplate + include Middleman::Features::FrontMatter::YamlAware + end + + class RDiscountTemplate < ::Tilt::RDiscountTemplate + include Middleman::Features::FrontMatter::YamlAware + end + + # TEXTILE + class RedClothTemplate < ::Tilt::RedClothTemplate + include Middleman::Features::FrontMatter::YamlAware + end +end \ No newline at end of file diff --git a/lib/middleman/server.rb b/lib/middleman/server.rb index 57af3d0a..75a66638 100644 --- a/lib/middleman/server.rb +++ b/lib/middleman/server.rb @@ -138,23 +138,6 @@ module Middleman end path.gsub(%r{^/}, '') end - - def self.parse_front_matter(path) - content = File.read(File.join(settings.views, path)) - - if content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m - content = $POSTMATCH - - begin - data = YAML.load($1) - rescue => e - puts "YAML Exception: #{e.message}" - end - end - - data ||= {} - [data, content] - end # Internal method to look for templates and evaluate them if found def process_request(options={}) @@ -170,9 +153,9 @@ module Middleman return end - full_file_path = "#{extensionless_path}.#{template_engine}" - data, content = self.class.parse_front_matter(full_file_path) + system_path = File.join(settings.views, full_file_path) + data, content = self.class.parse_front_matter(File.read(system_path)) %w(layout layout_engine).each do |opt| if data.has_key?(opt) diff --git a/middleman.gemspec b/middleman.gemspec index 81a35b99..6cfa44e8 100644 --- a/middleman.gemspec +++ b/middleman.gemspec @@ -18,12 +18,10 @@ Gem::Specification.new do |s| s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] - s.add_runtime_dependency("rack", ["~> 1.0"]) - s.add_runtime_dependency("thin", ["~> 1.2.0"]) unless defined?(JRUBY_VERSION) - s.add_runtime_dependency("kirk", ["~> 0.1.8"]) if defined?(JRUBY_VERSION) - s.add_runtime_dependency("shotgun", ["~> 0.8.0"]) + s.add_runtime_dependency("rack", ["~> 1.1"]) s.add_runtime_dependency("thor", ["~> 0.14.0"]) s.add_runtime_dependency("tilt", ["~> 1.3.1"]) + s.add_runtime_dependency("rdiscount", ["~> 1.6.8"]) s.add_runtime_dependency("sinatra", ["~> 1.2.0"]) s.add_runtime_dependency("padrino-core", ["~> 0.9.23"]) s.add_runtime_dependency("padrino-helpers", ["~> 0.9.23"]) @@ -36,7 +34,6 @@ Gem::Specification.new do |s| s.add_runtime_dependency("compass", ["~> 0.11.1"]) s.add_runtime_dependency("coffee-script", ["~> 2.2.0"]) s.add_runtime_dependency("httparty", ["~> 0.7.0"]) - # s.add_runtime_dependency("fssm", ["~> 0.2.0"]) s.add_development_dependency("cucumber", ["~> 0.10.0"]) s.add_development_dependency("rspec", [">= 0"]) s.add_development_dependency("rocco", [">= 0"]) unless defined?(JRUBY_VERSION)