Start adding rdoc

This commit is contained in:
Thomas Reynolds 2011-11-23 21:59:53 -08:00
parent f363e4f7a1
commit 00271c1cee
13 changed files with 267 additions and 128 deletions

2
.gitignore vendored
View file

@ -12,3 +12,5 @@ docs
fixtures/test-app/build fixtures/test-app/build
.*.swp .*.swp
build build
doc
.yardoc

5
.yardopts Normal file
View file

@ -0,0 +1,5 @@
lib/**/*.rb
--exclude lib/middleman/vendor
--exclude lib/middleman/extensions/automatic_image_sizes/fastimage.rb
--exclude lib/middleman/extensions/minify_css/cssmin.rb
--no-private

View file

@ -1,56 +1,3 @@
# Middleman is a static site renderer that provides all the conveniences of
# a modern web stack, like Ruby on Rails, while remaining focused on building
# the fastest, most-professional sites possible
#
# Install Middleman:
#
# gem install middleman
#
# To accomplish its goals, Middleman supports provides access to:
#
#### Command-line tool:
# * **middleman init**: A tool for creating to new static sites.
# * **middleman server**: A tool for rapidly developing your static site.
# * **middleman build**: A tool for exporting your site into optimized HTML, CSS & JS.
#
#### Tons of templating languages including:
# * ERB (.erb)
# * Interpolated String (.str)
# * Sass (.sass)
# * Scss (.scss)
# * Haml (.haml)
# * Slim (.slim)
# * Less CSS (.less)
# * Builder (.builder)
# * Liquid (.liquid)
# * RDiscount (.markdown)
# * RedCloth (.textile)
# * RDoc (.rdoc)
# * Radius (.radius)
# * Markaby (.mab)
# * Nokogiri (.nokogiri)
# * Mustache (.mustache)
# * CoffeeScript (.coffee)
#
#### Compile-time Optimiztions
# * Javascript Minifiers: YUI, Google Closure & UglifyJS
# * Smush.it Image Compression
# * CSS Minification
#
#### Robust Extensions:
# Add your own runtime and build-time extensions!
#
#### Next Steps:
# * [Visit the website]
# * [Read the wiki]
# * [Email the users group]
# * [Submit bug reports]
#
# [Visit the website]: http://middlemanapp.com
# [Read the wiki]: https://github.com/tdreyno/middleman/wiki
# [Email the users group]: https://convore.com/middleman/
# [Submit bug reports]: https://github.com/tdreyno/middleman/issues
# Setup our load paths # Setup our load paths
libdir = File.dirname(__FILE__) libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir) $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
@ -78,6 +25,7 @@ module Middleman
autoload :Page, "middleman/sitemap/page" autoload :Page, "middleman/sitemap/page"
autoload :Template, "middleman/sitemap/template" autoload :Template, "middleman/sitemap/template"
end end
module CoreExtensions module CoreExtensions
# File Change Notifier # File Change Notifier
autoload :FileWatcher, "middleman/core_extensions/file_watcher" autoload :FileWatcher, "middleman/core_extensions/file_watcher"
@ -153,8 +101,15 @@ module Middleman
autoload :SitemapTree, "middleman/extensions/sitemap_tree" autoload :SitemapTree, "middleman/extensions/sitemap_tree"
end end
# Where to look in gems for extensions to auto-register
EXTENSION_FILE = File.join("lib", "middleman_extension.rb") EXTENSION_FILE = File.join("lib", "middleman_extension.rb")
class << self class << self
# Automatically load extensions from available RubyGems
# which contain the EXTENSION_FILE
#
# @private
def load_extensions_in_path def load_extensions_in_path
extensions = rubygems_latest_specs.select do |spec| extensions = rubygems_latest_specs.select do |spec|
spec_has_file?(spec, EXTENSION_FILE) spec_has_file?(spec, EXTENSION_FILE)
@ -162,10 +117,14 @@ module Middleman
extensions.each do |spec| extensions.each do |spec|
require spec.name require spec.name
# $stderr.puts "require: #{spec.name}"
end end
end end
# Backwards compatible means of finding all the latest gemspecs
# available on the system
#
# @private
# @return [Array] Array of latest Gem::Specification
def rubygems_latest_specs def rubygems_latest_specs
# If newer Rubygems # If newer Rubygems
if ::Gem::Specification.respond_to? :latest_specs if ::Gem::Specification.respond_to? :latest_specs
@ -175,15 +134,32 @@ module Middleman
end end
end end
# Where a given Gem::Specification has a specific file. Used
# to discover extensions and Sprockets-supporting gems.
#
# @private
# @param [Gem::Specification]
# @param [String] Path to look for
# @return [Boolean] Whether the file exists
def spec_has_file?(spec, path) def spec_has_file?(spec, path)
full_path = File.join(spec.full_gem_path, path) full_path = File.join(spec.full_gem_path, path)
File.exists?(full_path) File.exists?(full_path)
end end
# Create a new Class which is based on Middleman::Base
# Used to create a safe sandbox into which extensions and
# configuration can be included later without impacting
# other classes and instances.
#
# @return [Class]
def server(&block) def server(&block)
Class.new(Middleman::Base) Class.new(Middleman::Base)
end end
# Creates a new Rack::Server
#
# @param [Hash] options to pass to Rack::Server.new
# @return [Rack::Server]
def start_server(options={}) def start_server(options={})
opts = { opts = {
:Port => options[:port] || 4567, :Port => options[:port] || 4567,
@ -204,5 +180,8 @@ module Middleman
end end
end end
# Make the VERSION string available
require "middleman/version" require "middleman/version"
# Automatically discover extensions in RubyGems
Middleman.load_extensions_in_path Middleman.load_extensions_in_path

View file

@ -1,68 +1,125 @@
# Built on Rack
require "rack" require "rack"
# Using Tilt for templating
require "tilt" require "tilt"
# Simple callback library
require "middleman/vendor/hooks-0.2.0/lib/hooks" require "middleman/vendor/hooks-0.2.0/lib/hooks"
# Use ActiveSupport JSON and Inflections
require "active_support" require "active_support"
require "active_support/json" require "active_support/json"
require "active_support/core_ext/string/inflections" require "active_support/core_ext/string/inflections"
# Core Middleman Class
class Middleman::Base class Middleman::Base
# Uses callbacks
include Hooks include Hooks
# Before request hook
define_hook :before define_hook :before
# Ready (all loading and parsing of extensions complete) hook
define_hook :ready define_hook :ready
# Initialized (after initialized() runs)
define_hook :initialized define_hook :initialized
class << self class << self
# Reset Rack setup
#
# @private
def reset! def reset!
@app = nil @app = nil
@prototype = nil @prototype = nil
end end
# The shared Rack instance being build
#
# @private
# @return [Rack::Builder]
def app def app
@app ||= Rack::Builder.new @app ||= Rack::Builder.new
end end
# Get the static instance
#
# @private
# @return [Middleman::Base]
def inst(&block) def inst(&block)
@inst ||= new(&block) @inst ||= new(&block)
end end
# Return built Rack app
#
# @private
# @return [Rack::Builder]
def to_rack_app(&block) def to_rack_app(&block)
inner_app = inst(&block) inner_app = inst(&block)
app.map("/") { run inner_app } app.map("/") { run inner_app }
app app
end end
# Prototype app. Used in config.ru
#
# @private
# @return [Rack::Builder]
def prototype def prototype
@prototype ||= to_rack_app @prototype ||= to_rack_app
end end
# Call prototype, use in config.ru
#
# @private
def call(env) def call(env)
prototype.call(env) prototype.call(env)
end end
# Use Rack middleware
#
# @param [Class] Middleware
def use(middleware, *args, &block) def use(middleware, *args, &block)
app.use(middleware, *args, &block) app.use(middleware, *args, &block)
end end
# Add Rack App mapped to specific path
#
# @param [String] Path to map
def map(map, &block) def map(map, &block)
app.map(map, &block) app.map(map, &block)
end end
# Mix-in helper methods. Accepts either a list of Modules
# and/or a block to be evaluated
def helpers(*extensions, &block) def helpers(*extensions, &block)
class_eval(&block) if block_given? class_eval(&block) if block_given?
include(*extensions) if extensions.any? include(*extensions) if extensions.any?
end end
# Access class-wide defaults
#
# @private
# @return [Hash] Hash of default values
def defaults def defaults
@defaults ||= {} @defaults ||= {}
end end
# Set class-wide defaults
#
# @param [Symbol] Unique key name
# @param Default value
def set(key, value) def set(key, value)
@defaults ||= {} @defaults ||= {}
@defaults[key] = value @defaults[key] = value
end end
end end
# Set attributes
#
# @param [Symbol] Name of the attribue
# @param Attribute value
def set(key, value=nil, &block) def set(key, value=nil, &block)
setter = "#{key}=".to_sym setter = "#{key}=".to_sym
self.class.send(:attr_accessor, key) if !respond_to?(setter) self.class.send(:attr_accessor, key) if !respond_to?(setter)
@ -70,27 +127,39 @@ class Middleman::Base
send(setter, value) send(setter, value)
end end
# Basic Sinatra config # Root project directory (overwritten in middleman build/server)
set :root, Dir.pwd set :root, Dir.pwd
# Name of the source directory
set :source, "source" set :source, "source"
# Set the environment from the environment. Defaults to :development, set
# to :build by the build process
set :environment, (ENV['MM_ENV'] && ENV['MM_ENV'].to_sym) || :development set :environment, (ENV['MM_ENV'] && ENV['MM_ENV'].to_sym) || :development
# Disable logging by default
set :logging, false set :logging, false
# Middleman-specific options # Which file should be used for directory indexes
set :index_file, "index.html" # What file responds to folder requests set :index_file, "index.html"
# Such as the homepage (/) or subfolders (/about/)
# These directories are passed directly to Compass # Location of javascripts within source. Used by Sprockets.
set :js_dir, "javascripts" # Where to look for javascript files set :js_dir, "javascripts"
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 # Location of stylesheets within source. Used by Compass.
set :http_prefix, "/" # During build, add a prefix for absolute paths set :css_dir, "stylesheets"
set :views, "source" # Location of images within source. Used by HTML helpers and Compass.
set :images_dir, "images"
set :default_features, [ # Where to build output files
set :build_dir, "build"
# Default prefix for building paths. Used by HTML helpers and Compass.
set :http_prefix, "/"
# Automatically loaded extensions
set :default_extensions, [
:lorem, :lorem,
# :sitemap_tree # :sitemap_tree
] ]
@ -135,44 +204,76 @@ class Middleman::Base
register Middleman::CoreExtensions::FrontMatter register Middleman::CoreExtensions::FrontMatter
# Built-in Extensions # Built-in Extensions
Middleman::Extensions.register(:asset_host) { Middleman::Extensions::AssetHost } Middleman::Extensions.register(:asset_host) {
Middleman::Extensions.register(:automatic_image_sizes) { Middleman::Extensions::AutomaticImageSizes } Middleman::Extensions::AssetHost }
Middleman::Extensions.register(:cache_buster) { Middleman::Extensions::CacheBuster } Middleman::Extensions.register(:automatic_image_sizes) {
Middleman::Extensions.register(:directory_indexes) { Middleman::Extensions::DirectoryIndexes } Middleman::Extensions::AutomaticImageSizes }
Middleman::Extensions.register(:lorem) { Middleman::Extensions::Lorem } Middleman::Extensions.register(:cache_buster) {
Middleman::Extensions.register(:minify_css) { Middleman::Extensions::MinifyCss } Middleman::Extensions::CacheBuster }
Middleman::Extensions.register(:minify_javascript) { Middleman::Extensions::MinifyJavascript } Middleman::Extensions.register(:directory_indexes) {
Middleman::Extensions.register(:relative_assets) { Middleman::Extensions::RelativeAssets } Middleman::Extensions::DirectoryIndexes }
Middleman::Extensions.register(:sitemap_tree) { Middleman::Extensions::SitemapTree } Middleman::Extensions.register(:lorem) {
Middleman::Extensions::Lorem }
Middleman::Extensions.register(:minify_css) {
Middleman::Extensions::MinifyCss }
Middleman::Extensions.register(:minify_javascript) {
Middleman::Extensions::MinifyJavascript }
Middleman::Extensions.register(:relative_assets) {
Middleman::Extensions::RelativeAssets }
Middleman::Extensions.register(:sitemap_tree) {
Middleman::Extensions::SitemapTree }
# Accessor for current path
attr_accessor :current_path
# Initialize the Middleman project
def initialize(&block) def initialize(&block)
# Current path defaults to nil, used in views.
@current_path = nil @current_path = nil
# Setup the default values from calls to set before initialization
self.class.superclass.defaults.each { |k,v| set k,v } self.class.superclass.defaults.each { |k,v| set k,v }
# Evaluate a passed block if given
instance_exec(&block) if block_given? instance_exec(&block) if block_given?
# Build expanded source path once paths have been parsed
set :source_dir, File.join(root, source) set :source_dir, File.join(root, source)
super super
# Run initialized callbacks
run_hook :initialized run_hook :initialized
end end
# Shared cache instance
#
# @private
# @return [Middleman::Cache] The cache
def cache def cache
@_cache ||= ::Middleman::Cache.new @_cache ||= ::Middleman::Cache.new
end end
# Rack env
attr :env attr :env
# Rack request
attr :req attr :req
# Rack response
attr :res attr :res
# Rack Interface # Rack Interface
#
# @private
# @param Rack environment
def call(env) def call(env)
# Store environment, request and response for later
@env = env @env = env
@req = Rack::Request.new(env) @req = Rack::Request.new(env)
@res = Rack::Response.new @res = Rack::Response.new
# Catch :halt exceptions and use that response if given
catch(:halt) do catch(:halt) do
process_request process_request
@ -181,52 +282,81 @@ class Middleman::Base
end end
end end
# Halt the current request and return a response
#
# @private
# @param [String] Reponse value
def halt(response) def halt(response)
throw :halt, response throw :halt, response
end end
# Convenience methods to check if we're in a mode # Whether we're in development mode
# @return [Boolean] If we're in dev mode
def development?; environment == :development; end def development?; environment == :development; end
# Whether we're in build mode
# @return [Boolean] If we're in build mode
def build?; environment == :build; end def build?; environment == :build; end
# Internal method to look for templates and evaluate them if found # Core repsonse method. We process the request, check with the sitemap,
# and return the correct file, response or status message.
#
# @private
def process_request def process_request
# Normalize the path and add index if we're looking at a directory # Normalize the path and add index if we're looking at a directory
@original_path = env["PATH_INFO"].dup @original_path = env["PATH_INFO"].dup
@request_path = full_path(env["PATH_INFO"].gsub("%20", " ")) @request_path = full_path(env["PATH_INFO"].gsub("%20", " "))
# Run before callbacks
run_hook :before run_hook :before
# Return 404 if not in sitemap
return not_found unless sitemap.exists?(@request_path) return not_found unless sitemap.exists?(@request_path)
# Get the page object for this path
sitemap_page = sitemap.page(@request_path) sitemap_page = sitemap.page(@request_path)
# Return 404 if this path is specifically ignored
return not_found if sitemap_page.ignored? return not_found if sitemap_page.ignored?
# Static File # If this path is a static file, send it immediately
return send_file(sitemap_page.source_file) unless sitemap_page.template? return send_file(sitemap_page.source_file) unless sitemap_page.template?
# Set the current path for use in helpers
@current_path = @request_path.dup @current_path = @request_path.dup
# Set a HTTP content type based on the request's extensions
content_type sitemap_page.mime_type content_type sitemap_page.mime_type
# Valid content is a 200 status
res.status = 200 res.status = 200
rqp = @request_path
# Write out the contents of the page
res.write sitemap_page.render res.write sitemap_page.render
# End the request
halt res.finish halt res.finish
end end
public
# Backwards compatibilty with old Sinatra template interface # Backwards compatibilty with old Sinatra template interface
#
# @return [Middleman::Base]
def settings def settings
self self
end end
# Whether we're logging
#
# @return [Boolean] If we're logging
def logging? def logging?
logging logging
end end
attr_accessor :current_path # Expand a path to include the index file if it's a directory
#
# @private
# @param [String] Request path
# @return [String] Path with index file if necessary
def full_path(path) def full_path(path)
cache.fetch(:full_path, path) do cache.fetch(:full_path, path) do
parts = path ? path.split('/') : [] parts = path ? path.split('/') : []
@ -237,12 +367,59 @@ public
end end
end end
# Sinatra/Padrino render method signature. Simply forwards to the sitemap
#
# @param [Symbol] Engine name
# @param [String] Path
# @param [Hash] Rendering options
# @param [Hash] Rendering locals
# @return [String] Output
def render(engine, data, options={}, locals={}, &block)
if sitemap.exists?(data)
sitemap.page(data).render(options, locals, &block)
else
throw "Could not find file to render: #{data}"
end
end
# Add a new mime-type for a specific extension
#
# @param [Symbol] File extension
# @param [String] Mime type
def mime_type(type, value=nil)
return type if type.nil? || type.to_s.include?('/')
type = ".#{type}" unless type.to_s[0] == ?.
return ::Rack::Mime.mime_type(type, nil) unless value
::Rack::Mime::MIME_TYPES[type] = value
end
protected
# Halt request and return 404
def not_found def not_found
@res.status == 404 @res.status == 404
@res.write "<html><body><h1>File Not Found</h1><p>#{@request_path}</p></body>" @res.write "<html><body><h1>File Not Found</h1><p>#{@request_path}</p></body>"
@res.finish @res.finish
end end
# Set helpers at the class level
def helpers(*extensions, &block)
self.class.helpers(*extensions, &block)
end
# Set middleware at the class level
def use(middleware, *args, &block)
self.class.use(middleware, *args, &block)
end
# Set mapped rack app at the class level
def map(map, &block)
self.class.map(map, &block)
end
# Immediately send static file
#
# @param [String] File to send
def send_file(path) def send_file(path)
matched_mime = mime_type(File.extname(path)) matched_mime = mime_type(File.extname(path))
matched_mime = "application/octet-stream" if matched_mime.nil? matched_mime = "application/octet-stream" if matched_mime.nil?
@ -253,22 +430,9 @@ public
halt file.serving(env) halt file.serving(env)
end end
# Sinatra/Padrino render method signature # Set the content type for the current request
def render(engine, data, options={}, locals={}, &block) #
if sitemap.exists?(data) # @param [String] Content type
sitemap.page(data).render(options, locals, &block)
else
throw "Could not find file to render: #{data}"
end
end
def mime_type(type, value=nil)
return type if type.nil? || type.to_s.include?('/')
type = ".#{type}" unless type.to_s[0] == ?.
return ::Rack::Mime.mime_type(type, nil) unless value
::Rack::Mime::MIME_TYPES[type] = value
end
def content_type(type = nil, params={}) def content_type(type = nil, params={})
return res['Content-Type'] unless type return res['Content-Type'] unless type
default = params.delete :default default = params.delete :default
@ -285,17 +449,4 @@ public
end end
res['Content-Type'] = mime_type res['Content-Type'] = mime_type
end end
# Forward to class level
def helpers(*extensions, &block)
self.class.helpers(*extensions, &block)
end
def use(middleware, *args, &block)
self.class.use(middleware, *args, &block)
end
def map(map, &block)
self.class.map(map, &block)
end
end end

View file

@ -77,7 +77,7 @@ module Middleman
def initialize(base, app, config={}, &block) def initialize(base, app, config={}, &block)
@app = app @app = app
source = @app.views source = @app.source
@destination = @app.build_dir @destination = @app.build_dir
@source = File.expand_path(base.find_in_source_paths(source.to_s)) @source = File.expand_path(base.find_in_source_paths(source.to_s))

View file

@ -13,11 +13,11 @@ module Middleman::CoreExtensions::Compass
config.project_path = root config.project_path = root
config.environment = :development config.environment = :development
config.cache_path = File.join(root, ".sass-cache") config.cache_path = File.join(root, ".sass-cache")
config.sass_dir = File.join(views, css_dir) config.sass_dir = File.join(source, css_dir)
config.css_dir = File.join(views, css_dir) config.css_dir = File.join(source, css_dir)
config.javascripts_dir = File.join(views, js_dir) config.javascripts_dir = File.join(source, js_dir)
config.fonts_dir = File.join(views, fonts_dir) config.fonts_dir = File.join(source, fonts_dir)
config.images_dir = File.join(views, images_dir) config.images_dir = File.join(source, images_dir)
config.http_images_path = if respond_to? :http_images_path config.http_images_path = if respond_to? :http_images_path
http_images_path http_images_path

View file

@ -32,7 +32,7 @@ module Middleman::CoreExtensions::Extensions
class << self class << self
def included(app) def included(app)
# app.set :default_features, [] # app.set :default_extensions, []
app.define_hook :after_configuration app.define_hook :after_configuration
app.define_hook :before_configuration app.define_hook :before_configuration
app.define_hook :build_config app.define_hook :build_config
@ -131,7 +131,7 @@ module Middleman::CoreExtensions::Extensions
run_hook :after_configuration run_hook :after_configuration
# Add in defaults # Add in defaults
default_features.each do |ext| default_extensions.each do |ext|
# activate ext # activate ext
end end

View file

@ -57,7 +57,7 @@ module Middleman::CoreExtensions::FrontMatter
def initialize(app) def initialize(app)
@app = app @app = app
@source = File.expand_path(@app.views, @app.root) @source = File.expand_path(@app.source, @app.root)
@local_data = {} @local_data = {}
end end

View file

@ -16,7 +16,7 @@ module Middleman::Extensions
http_prefix = http_images_path rescue images_dir http_prefix = http_images_path rescue images_dir
begin begin
real_path = File.join(views, images_dir, path) real_path = File.join(source, images_dir, path)
full_path = File.expand_path(real_path, root) full_path = File.expand_path(real_path, root)
http_prefix = http_images_path rescue images_dir http_prefix = http_images_path rescue images_dir
if File.exists? full_path if File.exists? full_path

View file

@ -7,7 +7,7 @@ module Middleman::Extensions
app.compass_config do |config| app.compass_config do |config|
config.asset_cache_buster do |path, real_path| config.asset_cache_buster do |path, real_path|
real_path = real_path.path if real_path.is_a? File real_path = real_path.path if real_path.is_a? File
real_path = real_path.gsub(File.join(root, build_dir), views) real_path = real_path.gsub(File.join(root, build_dir), source)
if File.readable?(real_path) if File.readable?(real_path)
File.mtime(real_path).strftime("%s") File.mtime(real_path).strftime("%s")
else else

View file

@ -26,7 +26,7 @@ module Middleman::Renderers::Sass
location_of_sass_file = if @context.build? location_of_sass_file = if @context.build?
File.expand_path(@context.build_dir, @context.root) File.expand_path(@context.build_dir, @context.root)
else else
File.expand_path(@context.views, @context.root) File.expand_path(@context.source, @context.root)
end end
parts = basename.split('.') parts = basename.split('.')

View file

@ -5,7 +5,7 @@ module Middleman::Sitemap
def initialize(app) def initialize(app)
@app = app @app = app
@cache = ::Middleman::Cache.new @cache = ::Middleman::Cache.new
@source = File.expand_path(@app.views, @app.root) @source = File.expand_path(@app.source, @app.root)
@pages = {} @pages = {}
end end

View file

@ -48,6 +48,8 @@ Gem::Specification.new do |s|
s.add_development_dependency("cucumber", ["~> 1.1.0"]) s.add_development_dependency("cucumber", ["~> 1.1.0"])
s.add_development_dependency("rake", ["~> 0.9.2"]) s.add_development_dependency("rake", ["~> 0.9.2"])
s.add_development_dependency("rspec", ["~> 2.7.0"]) s.add_development_dependency("rspec", ["~> 2.7.0"])
s.add_development_dependency("rdoc", ["~> 3.9.4"])
s.add_development_dependency("yard")
s.add_development_dependency("jquery-rails") s.add_development_dependency("jquery-rails")
s.add_development_dependency("bootstrap-rails", ["0.0.5"]) s.add_development_dependency("bootstrap-rails", ["0.0.5"])
end end