Revert "update docs"

This reverts commit 43ac01af5c.
This commit is contained in:
tdreyno 2009-10-26 09:33:38 -07:00
parent 43ac01af5c
commit cf39b2e2cb
49 changed files with 1372 additions and 0 deletions

4
lib/middleman.rb Normal file
View file

@ -0,0 +1,4 @@
libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
require 'middleman/base'

137
lib/middleman/base.rb Normal file
View file

@ -0,0 +1,137 @@
# Be nice to other library systems, like the wonderful Rip
require 'rubygems' unless ENV['NO_RUBYGEMS']
# We're riding on Sinatra, so let's include it
require 'sinatra/base'
module Middleman
class Base < Sinatra::Base
set :app_file, __FILE__
set :root, Dir.pwd
set :reload, false
set :logging, false
set :environment, ENV['MM_ENV'] || :development
set :supported_formats, %w(erb)
set :index_file, "index.html"
set :js_dir, "javascripts"
set :css_dir, "stylesheets"
set :images_dir, "images"
set :build_dir, "build"
set :http_prefix, "/"
use Rack::ConditionalGet if environment == :development
@@features = []
def self.enable(*opts)
@@features << opts
super
end
def self.disable(*opts)
@@features -= opts
super
end
# Rack helper for adding mime-types during local preview
def self.mime(ext, type)
ext = ".#{ext}" unless ext.to_s[0] == ?.
::Rack::Mime::MIME_TYPES[ext.to_s] = type
end
# Convenience function to discover if a tempalte exists for the requested renderer (haml, sass, etc)
def template_exists?(path, renderer=nil)
template_path = path.dup
template_path << ".#{renderer}" if renderer
File.exists? File.join(options.views, template_path)
end
# Base case renderer (do nothing), Should be over-ridden
module StaticRender
def render_path(path)
if template_exists?(path, :erb)
erb(path.to_sym)
else
false
end
end
end
include StaticRender
# This will match all requests not overridden in the project's init.rb
not_found do
# Normalize the path and add index if we're looking at a directory
path = request.path
path << options.index_file if path.match(%r{/$})
path.gsub!(%r{^/}, '')
# If the enabled renderers succeed, return the content, mime-type and an HTTP 200
if content = render_path(path)
content_type media_type(File.extname(path)), :charset => 'utf-8'
status 200
content
else
status 404
end
end
end
end
# Haml is required & includes helpers
require "middleman/haml"
require "middleman/sass"
require 'sinatra/content_for'
require 'middleman/helpers'
require 'middleman/rack/static'
require 'middleman/rack/sprockets'
class Middleman::Base
helpers Sinatra::ContentFor
helpers Middleman::Helpers
use Middleman::Rack::Static
use Middleman::Rack::Sprockets
enable :compass
require "middleman/features/compass"
@@features -= [:compass]
# Features disabled by default
disable :slickmap
disable :cache_buster
disable :minify_css
disable :minify_javascript
disable :relative_assets
disable :maruku
disable :smush_pngs
# Default build features
configure :build do
enable :minify_css
enable :minify_javascript
enable :cache_buster
end
def self.new(*args, &bk)
# Check for and evaluate local configuration
local_config = File.join(self.root, "init.rb")
if File.exists? local_config
puts "== Reading: Local config" if logging?
class_eval File.read(local_config)
end
# loop over enabled feature
@@features.flatten.each do |feature_name|
next unless send(:"#{feature_name}?")
puts "== Enabling: #{feature_name.capitalize}" if logging?
require "middleman/features/#{feature_name}"
end
::Compass.configuration do |config|
config.http_images_path = self.http_images_path rescue File.join(self.http_prefix, self.images_dir)
config.http_stylesheets_path = self.http_css_path rescue File.join(self.http_prefix, self.css_dir)
end
super
end
end

53
lib/middleman/builder.rb Normal file
View file

@ -0,0 +1,53 @@
require 'templater'
require 'middleman/templater+dynamic_renderer.rb'
# Placeholder for any methods the builder needs to abstract to allow feature integration
module Middleman
class Builder < ::Templater::Generator
# Define source and desintation
def self.source_root; Dir.pwd; end
def destination_root; File.join(Dir.pwd, Middleman::Base.build_dir); end
# Override template to ask middleman for the correct extension to output
def self.template(name, *args, &block)
return if args[0].include?('layout')
args.first.split('/').each do |part|
return if part[0,1] == '_'
end
if (args[0] === args[1])
args[1] = args[0].gsub("#{File.basename(Middleman::Base.views)}/", "")
.gsub("#{File.basename(Middleman::Base.public)}/", "")
if File.extname(args[1]) != ".js"
args[1] = args[1].gsub!(File.extname(args[1]), "") if File.basename(args[1]).split('.').length > 2
end
end
super(name, *args, &block)
end
def self.file(name, *args, &block)
if (args[0] === args[1])
args[1] = args[0].gsub("#{File.basename(Middleman::Base.views)}/", "")
.gsub("#{File.basename(Middleman::Base.public)}/", "")
end
super(name, *args, &block)
end
def self.init!
glob! File.basename(Middleman::Base.public), []
glob! File.basename(Middleman::Base.views), Middleman::Base.supported_formats
end
def after_run
end
end
module Generators
extend ::Templater::Manifold
desc "Build a static site"
add :build, ::Middleman::Builder
end
end

2
lib/middleman/config.ru Normal file
View file

@ -0,0 +1,2 @@
require 'middleman'
run Middleman::Base

View file

@ -0,0 +1,18 @@
class << Middleman::Base
alias_method :pre_cache_buster_asset_url, :asset_url
def asset_url(path, prefix="", request=nil)
http_path = pre_cache_buster_asset_url(path, prefix, request)
if http_path.include?("://") || !%w(.css .png .jpg .js .gif).include?(File.extname(http_path))
http_path
else
begin
prefix = self.images_dir if prefix == self.http_images_path
rescue
end
real_path = File.join(self.environment == "build" ? self.build_dir : self.public, prefix, path)
http_path << "?" + File.mtime(real_path).strftime("%s") if File.readable?(real_path)
http_path
end
end
end

View file

@ -0,0 +1,18 @@
class Middleman::Base
configure do
::Compass.configuration do |config|
images_location = (self.environment == "build") ? self.build_dir : self.public
config.project_path = Dir.pwd
config.sass_dir = File.join(File.basename(self.views), self.css_dir)
config.output_style = :nested
config.css_dir = File.join(File.basename(images_location), self.css_dir)
config.images_dir = File.join(File.basename(images_location), self.images_dir)
# File.expand_path(self.images_dir, self.public)
config.add_import_path(config.sass_dir)
end
::Compass.configure_sass_plugin!
end
end

View file

@ -0,0 +1,2 @@
# Errors to growl
# Build complete to growl

View file

@ -0,0 +1,39 @@
begin
require 'maruku'
rescue LoadError
puts "Maruku not available. Install it with: gem install maruku"
end
module Middleman
module Maruku
def self.included(base)
base.supported_formats << "maruku"
base.set :maruku, {}
end
def render_path(path)
if template_exists?(path, :maruku)
render :maruku, path.to_sym
else
super
end
end
private
def render_maruku(template, data, options, locals, &block)
maruku_src = render_erb(template, data, options, locals, &block)
instance = ::Maruku.new(maruku_src, options)
if block_given?
# render layout
instance.to_html_document
else
# render template
instance.to_html
end
end
end
class Base
include Middleman::Maruku
end
end

View file

@ -0,0 +1,9 @@
# Otherwise use YUI
# Fine a way to minify inline/css
class Middleman::Base
configure do
::Compass.configuration do |config|
config.output_style = :compressed
end
end
end

View file

@ -0,0 +1,35 @@
require "yui/compressor"
module Middleman
module Minified
module Javascript
include ::Haml::Filters::Base
def render_with_options(text, options)
compressor = ::YUI::JavaScriptCompressor.new(:munge => true)
data = compressor.compress(text)
<<END
<script type=#{options[:attr_wrapper]}text/javascript#{options[:attr_wrapper]}>#{data.chomp}</script>
END
end
end
end
class Builder
alias_method :pre_yui_after_run, :after_run
def after_run
pre_yui_after_run
compressor = ::YUI::JavaScriptCompressor.new(:munge => true)
Dir[File.join(Middleman::Base.build_dir, Middleman::Base.js_dir, "**", "*.js")].each do |path|
lines = IO.readlines(path)
if lines.length > 1
compressed_js = compressor.compress(lines.join($/))
File.open(path, 'w') { |f| f.write(compressed_js) }
say "<%= color('#{"[COMPRESSED]".rjust(12)}', :yellow) %> " + path.gsub(Middleman::Base.build_dir+"/", '')
end
end
end
end if Middleman::Base.environment == "build"
end
Middleman::Base.supported_formats << "js"

View file

@ -0,0 +1,30 @@
::Compass.configuration do |config|
config.relative_assets = true
end
class << Middleman::Base
alias_method :pre_relative_asset_url, :asset_url
def asset_url(path, prefix="", request=nil)
begin
prefix = self.images_dir if prefix == self.http_images_path
rescue
end
path = pre_relative_asset_url(path, prefix, request)
if path.include?("://")
path
else
path = path[1,path.length-1] if path[0,1] == '/'
request_path = request.path_info.dup
request_path << self.class.index_file if path.match(%r{/$})
request_path.gsub!(%r{^/}, '')
parts = request_path.split('/')
if parts.length > 1
"../" * (parts.length - 1) + path
else
path
end
end
end
end

View file

@ -0,0 +1,119 @@
begin
require 'slickmap'
::Compass.configure_sass_plugin!
rescue LoadError
puts "Slickmap not available. Install it with: gem install compass-slickmap"
end
if Middleman::Base.environment == "build"
Middleman::Builder.template :slickmap, "sitemap.html", "sitemap.html"
end
Entry = Struct.new(:dir, :children)
class Middleman::Base
def build_sitemap(&block)
@@utility = []
[recurse_sitemap(Middleman::Base.views, &block), @@utility]
end
def recurse_sitemap(path, &block)
bad_ext = path.split('.html')[1]
path = path.gsub(bad_ext, '') if bad_ext
entry = Entry.new(path, [])
#no "." or ".." dirs
Dir[File.join(path, "*")].each do |e|
next if !File.directory?(e) && !e.include?(".html")
if File.directory?(e)
entry.children << recurse_sitemap(e, &block)
elsif block_given?
how_to_handle = block.call(e)
if how_to_handle == :valid
entry.children << recurse_sitemap(e, &block)
elsif how_to_handle == :utility
bad_ext = e.split('.html')[1]
e = e.gsub(bad_ext, '') if bad_ext
@@utility << e.gsub(Middleman::Base.views + "/", '')
end
end
end
entry
end
helpers do
def sitemap_node(n, first=false)
if n.children.length < 1
if !first && File.extname(n.dir).length > 0
haml_tag :li do
path = n.dir.gsub(self.class.views, '')
haml_concat link_to(File.basename(path), path)
end
end
else
haml_tag(:li, :id => first ? "home" : nil) do
if first
haml_concat link_to("Homepage", "/" + self.class.index_file)
else
# we are a dir
index = n.children.find { |c| c.dir.include?(self.class.index_file) }
haml_concat link_to(index.dir.gsub(self.class.views + "/", '').gsub("/" + File.basename(index.dir), '').capitalize, index.dir.gsub(self.class.views, ''))
end
other_children = n.children.select { |c| !c.dir.include?(self.class.index_file) }
if other_children.length > 0
if first
other_children.each { |i| sitemap_node(i) }
else
haml_tag :ul do
other_children.each { |i| sitemap_node(i) }
end
end
end
end
end
end
end
get '/sitemap.html' do
# Return :utility to put it util top menu. False to ignore
@tree, @utility = build_sitemap do |file_name|
:valid
end
haml :sitemap, :layout => false
end
use_in_file_templates!
end
__END__
@@ sitemap
!!!
%html{ :xmlns => "http://www.w3.org/1999/xhtml" }
%head
%meta{ :content => "text/html; charset=utf-8", "http-equiv" => "Content-type" }
%title Sitemap
%style{ :type => "text/css" }
:sass
@import slickmap.sass
+slickmap
:javascript
window.onload = function() {
document.getElementById('primaryNav').className = "col" + document.querySelectorAll("#primaryNav > li:not(#home)").length;
};
%body
.logo
%h1= @project_name || "Sitemap"
- if @project_subtitle
%h2= @project_subtitle
- if @utility.length > 0
%ul#utilityNav
- @utility.each do |u|
%li= link_to u, u
%ul#primaryNav
- sitemap_node(@tree, true)

View file

@ -0,0 +1,58 @@
require 'json'
require 'open-uri'
begin
require 'httpclient'
rescue LoadError
puts "httpclient not available. Install it with: gem install httpclient"
end
module Middleman
module SmushPngs
def self.included(base)
base.supported_formats << "png"
end
def render_path(file)
if File.extname(file) == ".png"
file = File.join(options.public, file)
optimized = optimized_image_data_for(file)
begin
raise "Error: got larger" if size(file) < optimized.size
raise "Error: empty file downloaded" if optimized.size < 20
optimized
rescue
File.read(file)
end
else
super
end
end
protected
def size(file)
File.exist?(file) ? File.size(file) : 0
end
def optimized_image_data_for(file)
# I leave these urls here, just in case it stops working again...
# url = "http://smush.it/ws.php" # original, redirects to somewhere else..
url = 'http://ws1.adq.ac4.yahoo.com/ysmush.it/ws.php'
# url = "http://developer.yahoo.com/yslow/smushit/ws.php" # official but does not work
# url = "http://smushit.com/ysmush.it/ws.php" # used at the new page but does not hande uploads
# url = "http://smushit.eperf.vip.ac4.yahoo.com/ysmush.it/ws.php" # used at the new page but does not hande uploads
response = HTTPClient.post url, { 'files[]' => File.new(file) }
response = JSON.parse(response.body.content)
raise "smush.it: #{response['error']}" if response['error']
image_url = response['dest']
raise "no dest path found" unless image_url
open(image_url) { |source| source.read() }
end
end
class Base
include Middleman::SmushPngs
end
end

69
lib/middleman/haml.rb Normal file
View file

@ -0,0 +1,69 @@
require "haml"
module Middleman
module Haml
module Renderer
def self.included(base)
base.supported_formats << "haml"
base.helpers Middleman::Haml::Helpers
end
def render_path(path)
if template_exists?(path, :haml)
result = nil
begin
result = haml(path.to_sym, :layout => File.extname(path) != ".xml")
rescue ::Haml::Error => e
result = "Haml Error: #{e}"
result << "<pre>Backtrace: #{e.backtrace.join("\n")}</pre>"
end
result
else
super
end
end
end
module Helpers
def haml_partial(name, options = {})
item_name = name.to_sym
counter_name = "#{name}_counter".to_sym
if collection = options.delete(:collection)
collection.enum_for(:each_with_index).collect do |item,index|
haml_partial name, options.merge(:locals => {item_name => item, counter_name => index+1})
end.join
elsif object = options.delete(:object)
haml_partial name, options.merge(:locals => {item_name => object, counter_name => nil})
else
haml "_#{name}".to_sym, options.merge(:layout => false)
end
end
end
module Table
include ::Haml::Filters::Base
def render(text)
output = '<div class="table"><table cellspacing="0" cellpadding="0">'
line_num = 0
text.each_line do |line|
line_num += 1
next if line.strip.empty?
output << %Q{<tr class="#{(line_num % 2 == 0) ? "even" : "odd" }#{(line_num == 1) ? " first" : "" }">}
columns = line.split("|").map { |p| p.strip }
columns.each_with_index do |col, i|
output << %Q{<td class="col#{i+1}">#{col}</td>}
end
output << "</tr>"
end
output + "</table></div>"
end
end
end
end
class Middleman::Base
include Middleman::Haml::Renderer
end

55
lib/middleman/helpers.rb Normal file
View file

@ -0,0 +1,55 @@
module Middleman
class Base
def self.asset_url(path, prefix="", request=nil)
base_url = File.join(self.http_prefix, prefix)
path.include?("://") ? path : File.join(base_url, path)
end
end
module Helpers
def page_classes(*additional)
path = request.path_info
path << options.index_file if path.match(%r{/$})
path.gsub!(%r{^/}, '')
classes = []
parts = path.split('.')[0].split('/')
parts.each_with_index { |path, i| classes << parts.first(i+1).join('_') }
classes << "index" if classes.empty?
classes += additional unless additional.empty?
classes.join(' ')
end
def asset_url(path, prefix="")
self.class.asset_url(path, prefix, request)
end
def link_to(title, url="#", params={})
params.merge!(:href => url)
params = params.map { |k,v| %Q{#{k}="#{v}"}}.join(' ')
%Q{<a #{params}>#{title}</a>}
end
def image_tag(path, params={})
params[:alt] ||= ""
prefix = options.http_images_path rescue options.images_dir
params = params.merge(:src => asset_url(path, prefix))
params = params.map { |k,v| %Q{#{k}="#{v}"}}.join(' ')
"<img #{params} />"
end
def javascript_include_tag(path, params={})
params = params.merge(:src => asset_url(path, options.js_dir), :type => "text/javascript")
params = params.map { |k,v| %Q{#{k}="#{v}"}}.join(' ')
"<script #{params}></script>"
end
def stylesheet_link_tag(path, params={})
params[:rel] ||= "stylesheet"
params = params.merge(:href => asset_url(path, options.css_dir), :type => "text/css")
params = params.map { |k,v| %Q{#{k}="#{v}"}}.join(' ')
"<link #{params} />"
end
end
end

View file

@ -0,0 +1,29 @@
module Sprockets
class SourceFile
def source_lines
@lines ||= begin
lines = []
comments = []
File.open(pathname.absolute_location, 'rb') do |file|
file.each do |line|
lines << line = SourceLine.new(self, line, file.lineno)
if line.begins_pdoc_comment? || comments.any?
comments << line
end
if line.ends_multiline_comment?
if line.ends_pdoc_comment?
comments.each { |l| l.comment! }
end
comments.clear
end
end
end
lines
end
end
end
end

View file

@ -0,0 +1,34 @@
begin
require 'sprockets'
require 'middleman/rack/sprockets+ruby19' # Sprockets ruby 1.9 duckpunch
rescue LoadError
puts "Sprockets not available. Install it with: gem install sprockets"
end
module Middleman
module Rack
class Sprockets
def initialize(app, options={})
@app = app
end
def call(env)
path = env["PATH_INFO"]
source = File.join(Middleman::Base.views, path)
if path.match(/\.js$/) && File.exists?(source)
secretary = ::Sprockets::Secretary.new( :root => Middleman::Base.root,
:source_files => [ File.join("views", path) ],
:load_path => [ File.join("public", Middleman::Base.js_dir),
File.join("views", Middleman::Base.js_dir) ])
[200, { "Content-Type" => "text/javascript" }, [secretary.concatenation.to_s]]
else
@app.call(env)
end
end
end
end
end
Middleman::Base.supported_formats << "js"

View file

@ -0,0 +1,21 @@
module Middleman
module Rack
class Static
def initialize(app, options={})
@app = app
root = Middleman::Base.public
@file_server = ::Rack::File.new(root)
end
def call(env)
path = env["PATH_INFO"]
file_path = File.join(Middleman::Base.public, path)
if path.include?("favicon.ico") || (File.exists?(file_path) && !File.directory?(file_path))
@file_server.call(env)
else
@app.call(env)
end
end
end
end
end

70
lib/middleman/sass.rb Normal file
View file

@ -0,0 +1,70 @@
require "sass"
require 'compass'
module Middleman
module Sass
def self.included(base)
base.supported_formats << "sass"
end
def render_path(path)
if template_exists?(path, :sass)
begin
static_version = options.public + request.path_info
send_file(static_version) if File.exists? static_version
location_of_sass_file = options.environment == "build" ? File.join(options.build_dir, options.css_dir) : "public"
css_filename = File.join(Dir.pwd, location_of_sass_file) + request.path_info
sass(path.to_sym, ::Compass.sass_engine_options.merge({ :css_filename => css_filename }))
rescue Exception => e
sass_exception_string(e)
end
else
super
end
end
# Handle Sass errors
def sass_exception_string(e)
e_string = "#{e.class}: #{e.message}"
if e.is_a? ::Sass::SyntaxError
e_string << "\non line #{e.sass_line}"
if e.sass_filename
e_string << " of #{e.sass_filename}"
if File.exists?(e.sass_filename)
e_string << "\n\n"
min = [e.sass_line - 5, 0].max
begin
File.read(e.sass_filename).rstrip.split("\n")[
min .. e.sass_line + 5
].each_with_index do |line, i|
e_string << "#{min + i + 1}: #{line}\n"
end
rescue
e_string << "Couldn't read sass file: #{e.sass_filename}"
end
end
end
end
<<END
/*
#{e_string}
Backtrace:\n#{e.backtrace.join("\n")}
*/
body:before {
white-space: pre;
font-family: monospace;
content: "#{e_string.gsub('"', '\"').gsub("\n", '\\A ')}"; }
END
end
end
end
class Middleman::Base
include Middleman::Sass
end

View file

@ -0,0 +1,22 @@
<% if css_dir %>set :css_dir, "<%= css_dir -%>"<% end %>
<% if js_dir %>set :js_dir, "<%= js_dir -%>"<% end %>
<% if images_dir %>set :images_dir, "<%= images_dir -%>"<% end %>
# Helpers
helpers do
end
# Generic configuration
# enable :slickmap
# Build-specific configuration
configure :build do
# For example, change the Compass output style for deployment
# enable :minified_css
# Or use a different image path
# set :http_path, "/Content/images/"
# Disable cache buster
# disable :cache_buster
end

View file

@ -0,0 +1,4 @@
- content_for :head do
%title Custom head title
%h1 The Middleman is watching.

View file

@ -0,0 +1,7 @@
%html
%head
%title The Middleman!
= yield_content :head
%body
= yield

View file

@ -0,0 +1 @@
@import compass/reset.sass

View file

@ -0,0 +1,25 @@
require 'rack/test' # Use Rack::Test to access Sinatra without starting up a full server
# Monkey-patch to use a dynamic renderer
class Templater::Actions::File
def identical?
if exists?
return true if File.mtime(source) < File.mtime(destination)
FileUtils.identical?(source, destination)
else
false
end
end
end
class Templater::Actions::Template
def render
@render_cache ||= begin
# The default render just requests the page over Rack and writes the response
request_path = destination.gsub(File.join(Dir.pwd, Middleman::Base.build_dir), "")
browser = Rack::Test::Session.new(Rack::MockSession.new(Middleman::Base))
browser.get(request_path)
browser.last_response.body
end
end
end