2011-11-18 04:56:55 +01:00
|
|
|
require "rack"
|
|
|
|
require "tilt"
|
2011-09-09 08:06:22 +02:00
|
|
|
require "i18n"
|
2011-11-08 23:57:55 +01:00
|
|
|
require "hooks"
|
2011-09-09 08:06:22 +02:00
|
|
|
require "active_support"
|
|
|
|
require "active_support/json"
|
2011-11-18 04:56:55 +01:00
|
|
|
require "active_support/core_ext/string/inflections"
|
|
|
|
# require "active_support/core_ext/class/attribute_accessors"
|
2011-09-09 08:06:22 +02:00
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
class Middleman::Base
|
|
|
|
include Hooks
|
2011-11-18 22:38:18 +01:00
|
|
|
define_hook :before
|
2011-11-18 04:56:55 +01:00
|
|
|
define_hook :build_config
|
|
|
|
define_hook :development_config
|
|
|
|
|
2011-11-17 20:43:04 +01:00
|
|
|
class << self
|
2011-11-18 04:56:55 +01:00
|
|
|
def reset!
|
|
|
|
@app = nil
|
|
|
|
@prototype = nil
|
2011-11-17 20:43:04 +01:00
|
|
|
end
|
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
def app
|
|
|
|
@app ||= Rack::Builder.new
|
|
|
|
end
|
|
|
|
|
2011-11-18 09:34:56 +01:00
|
|
|
def inst(&block)
|
|
|
|
@inst ||= new(&block)
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_rack_app(&block)
|
|
|
|
app.run inst(&block)
|
|
|
|
app
|
|
|
|
end
|
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
def prototype
|
|
|
|
@prototype ||= app.to_app
|
|
|
|
end
|
2011-07-13 09:38:04 +02:00
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
def call(env)
|
|
|
|
prototype.call(env)
|
2011-07-13 09:38:04 +02:00
|
|
|
end
|
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
def use(middleware, *args, &block)
|
|
|
|
app.use(middleware, *args, &block)
|
2011-07-13 09:38:04 +02:00
|
|
|
end
|
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
def map(map, &block)
|
2011-11-18 22:38:18 +01:00
|
|
|
# app.map(map, &block)
|
2011-11-18 04:56:55 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def helpers(*extensions, &block)
|
|
|
|
class_eval(&block) if block_given?
|
|
|
|
include(*extensions) if extensions.any?
|
|
|
|
end
|
|
|
|
|
|
|
|
def defaults
|
|
|
|
@defaults ||= {}
|
|
|
|
end
|
|
|
|
|
|
|
|
def set(key, value)
|
|
|
|
@defaults ||= {}
|
|
|
|
@defaults[key] = value
|
2011-11-14 06:57:53 +01:00
|
|
|
end
|
|
|
|
|
2011-09-09 08:06:22 +02:00
|
|
|
def before_processing(name=:unnamed, idx=-1, &block)
|
2011-07-14 21:32:39 +02:00
|
|
|
@before_processes ||= []
|
2011-09-09 08:06:22 +02:00
|
|
|
@before_processes.insert(idx, [name, block])
|
2011-07-14 21:32:39 +02:00
|
|
|
end
|
|
|
|
|
2011-09-09 08:06:22 +02:00
|
|
|
def execute_before_processing!(inst, resolved_template)
|
2011-07-14 21:32:39 +02:00
|
|
|
@before_processes ||= []
|
|
|
|
|
2011-09-09 08:06:22 +02:00
|
|
|
@before_processes.all? do |name, block|
|
|
|
|
inst.instance_exec(resolved_template, &block)
|
2011-07-14 21:32:39 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
def configure(env, &block)
|
|
|
|
send("#{env}_config", &block)
|
2011-11-08 23:57:55 +01:00
|
|
|
end
|
2011-11-18 04:56:55 +01:00
|
|
|
end
|
|
|
|
|
2011-11-18 22:38:18 +01:00
|
|
|
def set(key, value=nil, &block)
|
2011-11-18 04:56:55 +01:00
|
|
|
setter = "#{key}=".to_sym
|
|
|
|
self.class.send(:attr_accessor, key) if !respond_to?(setter)
|
2011-11-18 22:38:18 +01:00
|
|
|
value = block if block_given?
|
2011-11-18 04:56:55 +01:00
|
|
|
send(setter, value)
|
|
|
|
end
|
|
|
|
|
|
|
|
def configure(env, &block)
|
|
|
|
self.class.configure(env, &block)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Basic Sinatra config
|
|
|
|
set :root, Dir.pwd
|
|
|
|
set :source, "source"
|
|
|
|
set :environment, (ENV['MM_ENV'] && ENV['MM_ENV'].to_sym) || :development
|
|
|
|
set :logging, false
|
|
|
|
|
|
|
|
# Middleman-specific options
|
|
|
|
set :index_file, "index.html" # What file responds to folder requests
|
|
|
|
# Such as the homepage (/) or subfolders (/about/)
|
|
|
|
|
|
|
|
# These directories are passed directly to Compass
|
|
|
|
set :js_dir, "javascripts" # Where to look for javascript files
|
|
|
|
set :css_dir, "stylesheets" # Where to look for CSS files
|
|
|
|
set :images_dir, "images" # Where to look for images
|
|
|
|
|
|
|
|
set :build_dir, "build" # Which folder are builds output to
|
|
|
|
set :http_prefix, nil # During build, add a prefix for absolute paths
|
|
|
|
|
|
|
|
set :views, "source"
|
|
|
|
|
|
|
|
set :default_features, [
|
|
|
|
:lorem,
|
|
|
|
# :sitemap_tree
|
|
|
|
]
|
|
|
|
|
|
|
|
# Default layout name
|
|
|
|
set :layout, :layout
|
|
|
|
|
|
|
|
# Activate custom features and extensions
|
|
|
|
include Middleman::CoreExtensions::Features
|
2011-11-17 20:43:04 +01:00
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
# Add Builder Callbacks
|
|
|
|
register Middleman::CoreExtensions::Builder
|
|
|
|
|
|
|
|
# Add Guard Callbacks
|
|
|
|
register Middleman::CoreExtensions::FileWatcher
|
|
|
|
|
|
|
|
# Sitemap
|
|
|
|
register Middleman::CoreExtensions::Sitemap
|
|
|
|
|
|
|
|
# Activate Data package
|
|
|
|
register Middleman::CoreExtensions::Data
|
|
|
|
|
|
|
|
# Setup custom rendering
|
|
|
|
register Middleman::CoreExtensions::Rendering
|
|
|
|
|
|
|
|
# Compass framework
|
|
|
|
register Middleman::CoreExtensions::Compass
|
|
|
|
|
|
|
|
# Sprockets asset handling
|
|
|
|
register Middleman::CoreExtensions::Sprockets
|
|
|
|
|
|
|
|
# Setup asset path pipeline
|
|
|
|
register Middleman::CoreExtensions::Assets
|
|
|
|
|
|
|
|
# Activate built-in helpers
|
|
|
|
register Middleman::CoreExtensions::DefaultHelpers
|
|
|
|
|
|
|
|
# with_layout and page routing
|
|
|
|
register Middleman::CoreExtensions::Routing
|
|
|
|
|
|
|
|
# Parse YAML from templates
|
|
|
|
register Middleman::CoreExtensions::FrontMatter
|
|
|
|
|
|
|
|
define_hook :initialized
|
|
|
|
def initialize(&block)
|
|
|
|
self.class.superclass.defaults.each do |k, v|
|
|
|
|
set(k, v)
|
2011-11-17 20:43:04 +01:00
|
|
|
end
|
2011-11-18 04:56:55 +01:00
|
|
|
|
2011-11-18 22:38:18 +01:00
|
|
|
instance_exec(&block) if block_given?
|
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
set :source_dir, File.join(root, source)
|
|
|
|
|
|
|
|
super
|
|
|
|
|
|
|
|
run_hook :initialized
|
2011-07-13 09:38:04 +02:00
|
|
|
end
|
|
|
|
|
2011-11-18 22:38:18 +01:00
|
|
|
attr :env
|
|
|
|
attr :req
|
|
|
|
attr :res
|
|
|
|
attr :options
|
|
|
|
attr :locals
|
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
def call(env)
|
|
|
|
@env = env
|
|
|
|
@req = Rack::Request.new(env)
|
|
|
|
@res = Rack::Response.new
|
|
|
|
|
2011-11-18 22:38:18 +01:00
|
|
|
catch(:halt) do
|
|
|
|
process_request
|
2011-11-18 04:56:55 +01:00
|
|
|
|
2011-11-18 22:38:18 +01:00
|
|
|
res.status = 404
|
|
|
|
res.finish
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def halt(response)
|
|
|
|
throw :halt, response
|
|
|
|
end
|
2011-11-17 20:43:04 +01:00
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
# Convenience methods to check if we're in a mode
|
|
|
|
def development?; environment == :development; end
|
|
|
|
def build?; environment == :build; end
|
|
|
|
|
|
|
|
# Internal method to look for templates and evaluate them if found
|
|
|
|
def process_request
|
2011-11-18 22:38:18 +01:00
|
|
|
run_hook :before
|
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
# Normalize the path and add index if we're looking at a directory
|
2011-11-18 22:38:18 +01:00
|
|
|
original_path = env["PATH_INFO"].dup
|
|
|
|
request_path = full_path(env["PATH_INFO"].gsub("%20", " "))
|
2011-11-18 04:56:55 +01:00
|
|
|
|
2011-11-18 09:34:56 +01:00
|
|
|
return not_found if sitemap.ignored_path?(request_path)
|
2011-11-18 04:56:55 +01:00
|
|
|
|
2011-11-18 09:34:56 +01:00
|
|
|
if sitemap.path_is_proxy?(request_path)
|
|
|
|
request_path = "/" + sitemap.path_target(request_path)
|
|
|
|
end
|
2011-11-18 04:56:55 +01:00
|
|
|
|
|
|
|
found_template = resolve_template(request_path)
|
|
|
|
return not_found unless found_template
|
|
|
|
|
2011-11-18 09:34:56 +01:00
|
|
|
@current_path = request_path
|
2011-11-18 04:56:55 +01:00
|
|
|
path, engine = found_template
|
|
|
|
|
|
|
|
# Static File
|
|
|
|
return send_file(path) if engine.nil?
|
2011-11-08 23:57:55 +01:00
|
|
|
|
2011-11-18 09:34:56 +01:00
|
|
|
return unless self.class.execute_before_processing!(self, found_template)
|
2011-11-18 04:56:55 +01:00
|
|
|
|
2011-11-18 09:34:56 +01:00
|
|
|
context = sitemap.get_context(original_path) || {}
|
2011-11-18 04:56:55 +01:00
|
|
|
|
2011-11-18 22:38:18 +01:00
|
|
|
@options = context.has_key?(:options) ? context[:options] : {}
|
|
|
|
@locals = context.has_key?(:locals) ? context[:locals] : {}
|
2011-11-18 09:34:56 +01:00
|
|
|
|
|
|
|
provides_metadata.each do |callback, matcher|
|
|
|
|
next if !matcher.nil? && !path.match(matcher)
|
2011-11-18 22:38:18 +01:00
|
|
|
instance_exec(path, &callback)
|
2011-11-18 09:34:56 +01:00
|
|
|
end
|
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
local_layout = if options.has_key?(:layout)
|
|
|
|
options[:layout]
|
2011-11-18 09:34:56 +01:00
|
|
|
elsif %w(.js .css).include?(File.extname(request_path))
|
|
|
|
false
|
2011-11-18 04:56:55 +01:00
|
|
|
else
|
|
|
|
layout
|
2011-10-04 02:09:27 +02:00
|
|
|
end
|
2011-11-14 06:57:53 +01:00
|
|
|
|
2011-11-18 09:34:56 +01:00
|
|
|
if context.has_key?(:block) && context[:block]
|
|
|
|
instance_eval(&context[:block])
|
|
|
|
end
|
2011-11-18 04:56:55 +01:00
|
|
|
|
|
|
|
# content_type mime_type(File.extname(request_path))
|
2011-11-18 22:38:18 +01:00
|
|
|
res.status = 200
|
2011-11-18 04:56:55 +01:00
|
|
|
|
2011-11-18 09:34:56 +01:00
|
|
|
output = if local_layout
|
2011-11-18 04:56:55 +01:00
|
|
|
layout_engine = if options.has_key?(:layout_engine)
|
|
|
|
options[:layout_engine]
|
|
|
|
else
|
|
|
|
engine
|
2011-11-14 06:57:53 +01:00
|
|
|
end
|
2011-09-09 08:06:22 +02:00
|
|
|
|
2011-11-18 09:34:56 +01:00
|
|
|
layout_path, *etc = resolve_template(local_layout, :preferred_engine => layout_engine)
|
|
|
|
|
|
|
|
throw "Could not locate layout: #{local_layout}" unless layout_path
|
2011-11-18 04:56:55 +01:00
|
|
|
|
|
|
|
render(layout_path, locals) do
|
|
|
|
render(path, locals)
|
2011-11-14 06:57:53 +01:00
|
|
|
end
|
2011-11-18 04:56:55 +01:00
|
|
|
else
|
|
|
|
render(path, locals)
|
|
|
|
end
|
|
|
|
|
2011-11-18 22:38:18 +01:00
|
|
|
res.write output
|
|
|
|
halt res.finish
|
2011-11-18 04:56:55 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
public
|
2011-11-18 09:34:56 +01:00
|
|
|
def extensionless_path(file)
|
|
|
|
@_extensionless_path_cache ||= {}
|
|
|
|
|
|
|
|
if @_extensionless_path_cache.has_key?(file)
|
|
|
|
@_extensionless_path_cache[file]
|
|
|
|
else
|
|
|
|
path = file.dup
|
|
|
|
end_of_the_line = false
|
|
|
|
while !end_of_the_line
|
|
|
|
file_extension = File.extname(path)
|
2011-11-18 04:56:55 +01:00
|
|
|
|
2011-11-18 09:34:56 +01:00
|
|
|
if ::Tilt.mappings.has_key?(file_extension.gsub(/^\./, ""))
|
|
|
|
path = path.sub(file_extension, "")
|
|
|
|
else
|
|
|
|
end_of_the_line = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@_extensionless_path_cache[file] = path
|
|
|
|
path
|
|
|
|
end
|
|
|
|
end
|
2011-11-18 04:56:55 +01:00
|
|
|
|
2011-11-18 09:34:56 +01:00
|
|
|
def logging?
|
|
|
|
logging
|
|
|
|
end
|
|
|
|
|
|
|
|
def current_path
|
|
|
|
@current_path || nil
|
|
|
|
end
|
|
|
|
|
|
|
|
def raw_templates_cache
|
|
|
|
@_raw_templates_cache ||= {}
|
|
|
|
end
|
|
|
|
|
|
|
|
def read_raw_template(path)
|
|
|
|
if !raw_templates_cache.has_key?(path)
|
|
|
|
raw_templates_cache[path] = File.read(path)
|
|
|
|
end
|
|
|
|
|
|
|
|
raw_templates_cache[path]
|
|
|
|
end
|
|
|
|
|
|
|
|
# def compiled_templates_cache
|
|
|
|
# @_compiled_templates_cache ||= {}
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# def read_compiled_template(path, locals, options, &block)
|
|
|
|
# key = [path, locals, options]
|
|
|
|
#
|
|
|
|
# if !raw_templates_cache.has_key?(key)
|
|
|
|
# raw_templates_cache[key] = yield
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# raw_templates_cache[key]
|
|
|
|
# end
|
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
def full_path(path)
|
|
|
|
parts = path ? path.split('/') : []
|
|
|
|
if parts.last.nil? || parts.last.split('.').length == 1
|
|
|
|
path = File.join(path, index_file)
|
|
|
|
end
|
|
|
|
"/" + path.sub(%r{^/}, '')
|
|
|
|
end
|
|
|
|
|
|
|
|
def not_found
|
|
|
|
@res.status == 404
|
2011-11-18 22:38:18 +01:00
|
|
|
@res.write "<html><body><h1>File Not Found</h1><p>#{env["PATH_INFO"]}</p></body>"
|
2011-11-18 04:56:55 +01:00
|
|
|
@res.finish
|
|
|
|
end
|
|
|
|
|
|
|
|
def resolve_template(request_path, options={})
|
|
|
|
request_path = request_path.to_s
|
|
|
|
@_resolved_templates ||= {}
|
|
|
|
if !@_resolved_templates.has_key?(request_path)
|
|
|
|
relative_path = request_path.sub(%r{^/}, "")
|
|
|
|
on_disk_path = File.expand_path(relative_path, source_dir)
|
2011-11-14 06:57:53 +01:00
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
preferred_engine = if options.has_key?(:preferred_engine)
|
|
|
|
extension_class = Tilt[options[:preferred_engine]]
|
|
|
|
matched_exts = []
|
2011-11-14 06:57:53 +01:00
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
# TODO: Cache this
|
|
|
|
Tilt.mappings.each do |ext, engines|
|
|
|
|
next unless engines.include? extension_class
|
|
|
|
matched_exts << ext
|
2011-07-13 09:38:04 +02:00
|
|
|
end
|
2011-11-18 04:56:55 +01:00
|
|
|
|
|
|
|
"{" + matched_exts.join(",") + "}"
|
2011-07-13 09:38:04 +02:00
|
|
|
else
|
2011-11-18 04:56:55 +01:00
|
|
|
"*"
|
2011-07-13 09:38:04 +02:00
|
|
|
end
|
2011-11-10 21:26:20 +01:00
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
path_with_ext = on_disk_path + "." + preferred_engine
|
|
|
|
|
|
|
|
found_engine = nil
|
|
|
|
found_path = Dir[path_with_ext].find do |path|
|
|
|
|
::Tilt[path]
|
|
|
|
end
|
|
|
|
|
|
|
|
result = if found_path || File.exists?(on_disk_path)
|
|
|
|
engine = found_path ? File.extname(found_path)[1..-1].to_sym : nil
|
|
|
|
[ found_path || on_disk_path, engine ]
|
|
|
|
else
|
|
|
|
false
|
2011-11-14 06:57:53 +01:00
|
|
|
end
|
|
|
|
|
2011-11-18 04:56:55 +01:00
|
|
|
@_resolved_templates[request_path] = result
|
|
|
|
end
|
|
|
|
|
|
|
|
@_resolved_templates[request_path]
|
|
|
|
end
|
|
|
|
|
|
|
|
def send_file(path)
|
|
|
|
|
|
|
|
# matched_mime = mime_type(File.extname(request_path))
|
|
|
|
# matched_mime = "application/octet-stream" if matched_mime.nil?
|
|
|
|
# content_type matched_mime
|
|
|
|
|
|
|
|
file = ::Rack::File.new nil
|
|
|
|
file.path = path
|
2011-11-18 22:38:18 +01:00
|
|
|
file.serving(env)
|
2011-11-18 04:56:55 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def render(path, locals = {}, options = {}, &block)
|
|
|
|
path = path.to_s
|
2011-11-18 09:34:56 +01:00
|
|
|
|
|
|
|
body = read_raw_template(path)
|
|
|
|
template = ::Tilt.new(path, 1, options) { body }
|
2011-11-18 04:56:55 +01:00
|
|
|
template.render(self, locals, &block)
|
|
|
|
end
|
2011-07-13 09:38:04 +02:00
|
|
|
end
|