Merge pull request #424 from middleman/remove_ruby_autoload
Remove usage of autoload statement in favor of require
This commit is contained in:
commit
b486b5a31d
|
@ -3,7 +3,12 @@ libdir = File.expand_path(File.dirname(__FILE__))
|
||||||
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
||||||
|
|
||||||
# Top-level Middleman namespace
|
# Top-level Middleman namespace
|
||||||
module Middleman; end
|
module Middleman
|
||||||
|
|
||||||
|
# Backwards compatibility namespace
|
||||||
|
module Features; end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
require "middleman-core/version"
|
require "middleman-core/version"
|
||||||
require "middleman-core/util"
|
require "middleman-core/util"
|
||||||
|
|
|
@ -9,6 +9,8 @@ require "middleman-core/vendor/hooks-0.2.0/lib/hooks"
|
||||||
|
|
||||||
require "middleman-core/sitemap"
|
require "middleman-core/sitemap"
|
||||||
|
|
||||||
|
require "middleman-core/core_extensions"
|
||||||
|
|
||||||
# Core Middleman Class
|
# Core Middleman Class
|
||||||
module Middleman
|
module Middleman
|
||||||
class Application
|
class Application
|
||||||
|
@ -160,14 +162,35 @@ module Middleman
|
||||||
register Middleman::CoreExtensions::I18n
|
register Middleman::CoreExtensions::I18n
|
||||||
|
|
||||||
# Built-in Extensions
|
# Built-in Extensions
|
||||||
Middleman::Extensions.register(:directory_indexes) {
|
|
||||||
Middleman::Extensions::DirectoryIndexes }
|
# Provide Apache-style index.html files for directories
|
||||||
Middleman::Extensions.register(:lorem) {
|
Middleman::Extensions.register(:directory_indexes) do
|
||||||
Middleman::Extensions::Lorem }
|
require "middleman-core/extensions/directory_indexes"
|
||||||
Middleman::Extensions.register(:automatic_image_sizes) {
|
Middleman::Extensions::DirectoryIndexes
|
||||||
Middleman::Extensions::AutomaticImageSizes }
|
end
|
||||||
Middleman::Extensions.register(:asset_host) {
|
|
||||||
Middleman::Extensions::AssetHost }
|
# Lorem provides a handful of helpful prototyping methods to generate
|
||||||
|
# words, paragraphs, fake images, names and email addresses.
|
||||||
|
Middleman::Extensions.register(:lorem) do
|
||||||
|
require "middleman-core/extensions/lorem"
|
||||||
|
Middleman::Extensions::Lorem
|
||||||
|
end
|
||||||
|
|
||||||
|
# AutomaticImageSizes inspects the images used in your dynamic templates
|
||||||
|
# and automatically adds width and height attributes to their HTML
|
||||||
|
# elements.
|
||||||
|
Middleman::Extensions.register(:automatic_image_sizes) do
|
||||||
|
require "middleman-core/extensions/automatic_image_sizes"
|
||||||
|
Middleman::Extensions::AutomaticImageSizes
|
||||||
|
end
|
||||||
|
|
||||||
|
# AssetHost allows you to setup multiple domains to host your static
|
||||||
|
# assets. Calls to asset paths in dynamic templates will then rotate
|
||||||
|
# through each of the asset servers to better spread the load.
|
||||||
|
Middleman::Extensions.register(:asset_host) do
|
||||||
|
require "middleman-core/extensions/asset_host"
|
||||||
|
Middleman::Extensions::AssetHost
|
||||||
|
end
|
||||||
|
|
||||||
# Initialize the Middleman project
|
# Initialize the Middleman project
|
||||||
def initialize(&block)
|
def initialize(&block)
|
||||||
|
|
|
@ -5,13 +5,6 @@ require "thor/group"
|
||||||
# CLI Module
|
# CLI Module
|
||||||
module Middleman::Cli
|
module Middleman::Cli
|
||||||
|
|
||||||
module Cli
|
|
||||||
autoload :Build, "middleman-core/cli/build"
|
|
||||||
autoload :Init, "middleman-core/cli/init"
|
|
||||||
autoload :Extension, "middleman-core/cli/extension"
|
|
||||||
autoload :Server, "middleman-core/cli/server"
|
|
||||||
end
|
|
||||||
|
|
||||||
# The base task from which everything else etends
|
# The base task from which everything else etends
|
||||||
class Base < Thor
|
class Base < Thor
|
||||||
|
|
||||||
|
|
39
middleman-core/lib/middleman-core/core_extensions.rb
Normal file
39
middleman-core/lib/middleman-core/core_extensions.rb
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
# Rack Request
|
||||||
|
require "middleman-core/core_extensions/request"
|
||||||
|
|
||||||
|
# File Change Notifier
|
||||||
|
require "middleman-core/core_extensions/file_watcher"
|
||||||
|
|
||||||
|
# Add Builder callbacks
|
||||||
|
require "middleman-core/core_extensions/builder"
|
||||||
|
|
||||||
|
# Custom Feature API
|
||||||
|
require "middleman-core/core_extensions/extensions"
|
||||||
|
|
||||||
|
# Asset Path Pipeline
|
||||||
|
require "middleman-core/core_extensions/assets"
|
||||||
|
|
||||||
|
# Data looks at the data/ folder for YAML files and makes them available
|
||||||
|
# to dynamic requests.
|
||||||
|
require "middleman-core/core_extensions/data"
|
||||||
|
|
||||||
|
# Parse YAML from templates
|
||||||
|
require "middleman-core/core_extensions/front_matter"
|
||||||
|
|
||||||
|
# External helpers looks in the helpers/ folder for helper modules
|
||||||
|
require "middleman-core/core_extensions/external_helpers"
|
||||||
|
|
||||||
|
# DefaultHelpers are the built-in dynamic template helpers.
|
||||||
|
require "middleman-core/core_extensions/default_helpers"
|
||||||
|
|
||||||
|
# Extended version of Padrino's rendering
|
||||||
|
require "middleman-core/core_extensions/rendering"
|
||||||
|
|
||||||
|
# Pass custom options to views
|
||||||
|
require "middleman-core/core_extensions/routing"
|
||||||
|
|
||||||
|
# Catch and show exceptions at the Rack level
|
||||||
|
require "middleman-core/core_extensions/show_exceptions"
|
||||||
|
|
||||||
|
# i18n
|
||||||
|
require "middleman-core/core_extensions/i18n"
|
|
@ -1,39 +1,43 @@
|
||||||
# Base helper to manipulate asset paths
|
module Middleman
|
||||||
module Middleman::CoreExtensions::Assets
|
module CoreExtensions
|
||||||
|
|
||||||
# Extension registered
|
|
||||||
class << self
|
|
||||||
# @private
|
|
||||||
def registered(app)
|
|
||||||
# Disable Padrino cache buster
|
|
||||||
app.set :asset_stamp, false
|
|
||||||
|
|
||||||
# Include helpers
|
|
||||||
app.send :include, InstanceMethod
|
|
||||||
end
|
|
||||||
alias :included :registered
|
|
||||||
end
|
|
||||||
|
|
||||||
# Methods to be mixed-in to Middleman::Application
|
|
||||||
module InstanceMethod
|
|
||||||
|
|
||||||
# Get the URL of an asset given a type/prefix
|
# Base helper to manipulate asset paths
|
||||||
#
|
module Assets
|
||||||
# @param [String] path The path (such as "photo.jpg")
|
|
||||||
# @param [String] prefix The type prefix (such as "images")
|
# Extension registered
|
||||||
# @return [String] The fully qualified asset url
|
class << self
|
||||||
def asset_url(path, prefix="")
|
def registered(app)
|
||||||
# Don't touch assets which already have a full path
|
# Disable Padrino cache buster
|
||||||
if path.include?("//")
|
app.set :asset_stamp, false
|
||||||
path
|
|
||||||
else # rewrite paths to use their destination path
|
# Include helpers
|
||||||
path = File.join(prefix, path)
|
app.send :include, InstanceMethod
|
||||||
if resource = sitemap.find_resource_by_path(path)
|
|
||||||
path = resource.destination_path
|
|
||||||
end
|
end
|
||||||
|
alias :included :registered
|
||||||
|
end
|
||||||
|
|
||||||
|
# Methods to be mixed-in to Middleman::Application
|
||||||
|
module InstanceMethod
|
||||||
|
|
||||||
|
# Get the URL of an asset given a type/prefix
|
||||||
|
#
|
||||||
|
# @param [String] path The path (such as "photo.jpg")
|
||||||
|
# @param [String] prefix The type prefix (such as "images")
|
||||||
|
# @return [String] The fully qualified asset url
|
||||||
|
def asset_url(path, prefix="")
|
||||||
|
# Don't touch assets which already have a full path
|
||||||
|
if path.include?("//")
|
||||||
|
path
|
||||||
|
else # rewrite paths to use their destination path
|
||||||
|
path = File.join(prefix, path)
|
||||||
|
if resource = sitemap.find_resource_by_path(path)
|
||||||
|
path = resource.destination_path
|
||||||
|
end
|
||||||
|
|
||||||
File.join(http_prefix, path)
|
File.join(http_prefix, path)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,12 +1,17 @@
|
||||||
# Convenience methods to allow config.rb to talk to the Builder
|
module Middleman
|
||||||
module Middleman::CoreExtensions::Builder
|
module CoreExtensions
|
||||||
|
|
||||||
|
# Convenience methods to allow config.rb to talk to the Builder
|
||||||
|
module Builder
|
||||||
|
|
||||||
# Extension registered
|
# Extension registered
|
||||||
class << self
|
class << self
|
||||||
# @private
|
# @private
|
||||||
def registered(app)
|
def registered(app)
|
||||||
app.define_hook :after_build
|
app.define_hook :after_build
|
||||||
|
end
|
||||||
|
alias :included :registered
|
||||||
|
end
|
||||||
end
|
end
|
||||||
alias :included :registered
|
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,175 +1,180 @@
|
||||||
# Data formats
|
module Middleman
|
||||||
require "yaml"
|
module CoreExtensions
|
||||||
require "active_support/json"
|
|
||||||
|
# The data extension parses YAML and JSON files in the data/ directory
|
||||||
# The data extension parses YAML and JSON files in the data/ directory
|
# and makes them available to config.rb, templates and extensions
|
||||||
# and makes them available to config.rb, templates and extensions
|
module Data
|
||||||
module Middleman::CoreExtensions::Data
|
|
||||||
|
|
||||||
# Extension registered
|
# Extension registered
|
||||||
class << self
|
class << self
|
||||||
# @private
|
# @private
|
||||||
def registered(app)
|
def registered(app)
|
||||||
app.set :data_dir, "data"
|
# Data formats
|
||||||
app.send :include, InstanceMethods
|
require "yaml"
|
||||||
end
|
require "active_support/json"
|
||||||
alias :included :registered
|
|
||||||
end
|
app.set :data_dir, "data"
|
||||||
|
app.send :include, InstanceMethods
|
||||||
|
end
|
||||||
|
alias :included :registered
|
||||||
|
end
|
||||||
|
|
||||||
# Instance methods
|
# Instance methods
|
||||||
module InstanceMethods
|
module InstanceMethods
|
||||||
# Setup data files before anything else so they are available when
|
# Setup data files before anything else so they are available when
|
||||||
# parsing config.rb
|
# parsing config.rb
|
||||||
def initialize
|
def initialize
|
||||||
self.files.changed DataStore.matcher do |file|
|
self.files.changed DataStore.matcher do |file|
|
||||||
self.data.touch_file(file) if file.match(%r{^#{self.data_dir}\/})
|
self.data.touch_file(file) if file.match(%r{^#{self.data_dir}\/})
|
||||||
end
|
end
|
||||||
|
|
||||||
self.files.deleted DataStore.matcher do |file|
|
self.files.deleted DataStore.matcher do |file|
|
||||||
self.data.remove_file(file) if file.match(%r{^#{self.data_dir}\/})
|
self.data.remove_file(file) if file.match(%r{^#{self.data_dir}\/})
|
||||||
end
|
end
|
||||||
|
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
# The data object
|
# The data object
|
||||||
#
|
#
|
||||||
# @return [DataStore]
|
# @return [DataStore]
|
||||||
def data
|
def data
|
||||||
@_data ||= DataStore.new(self)
|
@_data ||= DataStore.new(self)
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# The core logic behind the data extension.
|
|
||||||
class DataStore
|
|
||||||
|
|
||||||
# Static methods
|
|
||||||
class << self
|
|
||||||
|
|
||||||
# The regex which tells Middleman which files are for data
|
|
||||||
#
|
|
||||||
# @return [Regexp]
|
|
||||||
def matcher
|
|
||||||
%r{[\w-]+\.(yml|yaml|json)$}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Store static data hash
|
|
||||||
#
|
|
||||||
# @param [Symbol] name Name of the data, used for namespacing
|
|
||||||
# @param [Hash] content The content for this data
|
|
||||||
# @return [void]
|
|
||||||
def store(name=nil, content=nil)
|
|
||||||
@_local_sources ||= {}
|
|
||||||
@_local_sources[name.to_s] = content unless name.nil? || content.nil?
|
|
||||||
@_local_sources
|
|
||||||
end
|
|
||||||
|
|
||||||
# Store callback-based data
|
|
||||||
#
|
|
||||||
# @param [Symbol] name Name of the data, used for namespacing
|
|
||||||
# @param [Proc] proc The callback which will return data
|
|
||||||
# @return [void]
|
|
||||||
def callbacks(name=nil, proc=nil)
|
|
||||||
@_callback_sources ||= {}
|
|
||||||
@_callback_sources[name.to_s] = proc unless name.nil? || proc.nil?
|
|
||||||
@_callback_sources
|
|
||||||
end
|
|
||||||
|
|
||||||
# Setup data store
|
|
||||||
#
|
|
||||||
# @param [Middleman::Application] app The current instance of Middleman
|
|
||||||
def initialize(app)
|
|
||||||
@app = app
|
|
||||||
@local_data = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update the internal cache for a given file path
|
|
||||||
#
|
|
||||||
# @param [String] file The file to be re-parsed
|
|
||||||
# @return [void]
|
|
||||||
def touch_file(file)
|
|
||||||
file = File.expand_path(file, @app.root)
|
|
||||||
extension = File.extname(file)
|
|
||||||
basename = File.basename(file, extension)
|
|
||||||
|
|
||||||
if %w(.yaml .yml).include?(extension)
|
|
||||||
data = YAML.load_file(file)
|
|
||||||
elsif extension == ".json"
|
|
||||||
data = ActiveSupport::JSON.decode(File.read(file))
|
|
||||||
else
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
@local_data[basename] = ::Middleman::Util.recursively_enhance(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Remove a given file from the internal cache
|
|
||||||
#
|
|
||||||
# @param [String] file The file to be cleared
|
|
||||||
# @return [void]
|
|
||||||
def remove_file(file)
|
|
||||||
extension = File.extname(file)
|
|
||||||
basename = File.basename(file, extension)
|
|
||||||
@local_data.delete(basename) if @local_data.has_key?(basename)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get a hash hash from either internal static data or a callback
|
|
||||||
#
|
|
||||||
# @param [String, Symbol] path The name of the data namespace
|
|
||||||
# @return [Hash, nil]
|
|
||||||
def data_for_path(path)
|
|
||||||
response = nil
|
|
||||||
|
|
||||||
@@local_sources ||= {}
|
|
||||||
@@callback_sources ||= {}
|
|
||||||
|
|
||||||
if self.store.has_key?(path.to_s)
|
|
||||||
response = self.store[path.to_s]
|
|
||||||
elsif self.callbacks.has_key?(path.to_s)
|
|
||||||
response = self.callbacks[path.to_s].call()
|
|
||||||
end
|
|
||||||
|
|
||||||
response
|
|
||||||
end
|
|
||||||
|
|
||||||
# "Magically" find namespaces of data if they exist
|
|
||||||
#
|
|
||||||
# @param [String] path The namespace to search for
|
|
||||||
# @return [Hash, nil]
|
|
||||||
def method_missing(path)
|
|
||||||
if @local_data.has_key?(path.to_s)
|
|
||||||
return @local_data[path.to_s]
|
|
||||||
else
|
|
||||||
result = data_for_path(path)
|
|
||||||
|
|
||||||
if result
|
|
||||||
return ::Middleman::Util.recursively_enhance(result)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
super
|
# The core logic behind the data extension.
|
||||||
end
|
class DataStore
|
||||||
|
|
||||||
# Convert all the data into a static hash
|
# Static methods
|
||||||
#
|
class << self
|
||||||
# @return [Hash]
|
|
||||||
def to_h
|
|
||||||
data = {}
|
|
||||||
|
|
||||||
self.store.each do |k, v|
|
# The regex which tells Middleman which files are for data
|
||||||
data[k] = data_for_path(k)
|
#
|
||||||
|
# @return [Regexp]
|
||||||
|
def matcher
|
||||||
|
%r{[\w-]+\.(yml|yaml|json)$}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Store static data hash
|
||||||
|
#
|
||||||
|
# @param [Symbol] name Name of the data, used for namespacing
|
||||||
|
# @param [Hash] content The content for this data
|
||||||
|
# @return [void]
|
||||||
|
def store(name=nil, content=nil)
|
||||||
|
@_local_sources ||= {}
|
||||||
|
@_local_sources[name.to_s] = content unless name.nil? || content.nil?
|
||||||
|
@_local_sources
|
||||||
|
end
|
||||||
|
|
||||||
|
# Store callback-based data
|
||||||
|
#
|
||||||
|
# @param [Symbol] name Name of the data, used for namespacing
|
||||||
|
# @param [Proc] proc The callback which will return data
|
||||||
|
# @return [void]
|
||||||
|
def callbacks(name=nil, proc=nil)
|
||||||
|
@_callback_sources ||= {}
|
||||||
|
@_callback_sources[name.to_s] = proc unless name.nil? || proc.nil?
|
||||||
|
@_callback_sources
|
||||||
|
end
|
||||||
|
|
||||||
|
# Setup data store
|
||||||
|
#
|
||||||
|
# @param [Middleman::Application] app The current instance of Middleman
|
||||||
|
def initialize(app)
|
||||||
|
@app = app
|
||||||
|
@local_data = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update the internal cache for a given file path
|
||||||
|
#
|
||||||
|
# @param [String] file The file to be re-parsed
|
||||||
|
# @return [void]
|
||||||
|
def touch_file(file)
|
||||||
|
file = File.expand_path(file, @app.root)
|
||||||
|
extension = File.extname(file)
|
||||||
|
basename = File.basename(file, extension)
|
||||||
|
|
||||||
|
if %w(.yaml .yml).include?(extension)
|
||||||
|
data = YAML.load_file(file)
|
||||||
|
elsif extension == ".json"
|
||||||
|
data = ActiveSupport::JSON.decode(File.read(file))
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
@local_data[basename] = ::Middleman::Util.recursively_enhance(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Remove a given file from the internal cache
|
||||||
|
#
|
||||||
|
# @param [String] file The file to be cleared
|
||||||
|
# @return [void]
|
||||||
|
def remove_file(file)
|
||||||
|
extension = File.extname(file)
|
||||||
|
basename = File.basename(file, extension)
|
||||||
|
@local_data.delete(basename) if @local_data.has_key?(basename)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get a hash hash from either internal static data or a callback
|
||||||
|
#
|
||||||
|
# @param [String, Symbol] path The name of the data namespace
|
||||||
|
# @return [Hash, nil]
|
||||||
|
def data_for_path(path)
|
||||||
|
response = nil
|
||||||
|
|
||||||
|
@@local_sources ||= {}
|
||||||
|
@@callback_sources ||= {}
|
||||||
|
|
||||||
|
if self.store.has_key?(path.to_s)
|
||||||
|
response = self.store[path.to_s]
|
||||||
|
elsif self.callbacks.has_key?(path.to_s)
|
||||||
|
response = self.callbacks[path.to_s].call()
|
||||||
|
end
|
||||||
|
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
# "Magically" find namespaces of data if they exist
|
||||||
|
#
|
||||||
|
# @param [String] path The namespace to search for
|
||||||
|
# @return [Hash, nil]
|
||||||
|
def method_missing(path)
|
||||||
|
if @local_data.has_key?(path.to_s)
|
||||||
|
return @local_data[path.to_s]
|
||||||
|
else
|
||||||
|
result = data_for_path(path)
|
||||||
|
|
||||||
|
if result
|
||||||
|
return ::Middleman::Util.recursively_enhance(result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convert all the data into a static hash
|
||||||
|
#
|
||||||
|
# @return [Hash]
|
||||||
|
def to_h
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
self.store.each do |k, v|
|
||||||
|
data[k] = data_for_path(k)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.callbacks.each do |k, v|
|
||||||
|
data[k] = data_for_path(k)
|
||||||
|
end
|
||||||
|
|
||||||
|
(@local_data || {}).each do |k, v|
|
||||||
|
data[k] = v
|
||||||
|
end
|
||||||
|
|
||||||
|
data
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self.callbacks.each do |k, v|
|
|
||||||
data[k] = data_for_path(k)
|
|
||||||
end
|
|
||||||
|
|
||||||
(@local_data || {}).each do |k, v|
|
|
||||||
data[k] = v
|
|
||||||
end
|
|
||||||
|
|
||||||
data
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,119 +1,123 @@
|
||||||
require "active_support/core_ext/integer/inflections"
|
module Middleman
|
||||||
require 'padrino-helpers'
|
module CoreExtensions
|
||||||
|
# Built-in helpers
|
||||||
|
module DefaultHelpers
|
||||||
|
|
||||||
# Built-in helpers
|
# Extension registered
|
||||||
module Middleman::CoreExtensions::DefaultHelpers
|
class << self
|
||||||
|
# @private
|
||||||
|
def registered(app)
|
||||||
|
require "active_support/core_ext/integer/inflections"
|
||||||
|
require 'padrino-helpers'
|
||||||
|
|
||||||
|
app.helpers ::Padrino::Helpers::OutputHelpers
|
||||||
|
app.helpers ::Padrino::Helpers::TagHelpers
|
||||||
|
app.helpers ::Padrino::Helpers::AssetTagHelpers
|
||||||
|
app.helpers ::Padrino::Helpers::FormHelpers
|
||||||
|
app.helpers ::Padrino::Helpers::FormatHelpers
|
||||||
|
app.helpers ::Padrino::Helpers::RenderHelpers
|
||||||
|
app.helpers ::Padrino::Helpers::NumberHelpers
|
||||||
|
# app.helpers ::Padrino::Helpers::TranslationHelpers
|
||||||
|
|
||||||
# Extension registered
|
app.helpers Helpers
|
||||||
class << self
|
end
|
||||||
# @private
|
alias :included :registered
|
||||||
def registered(app)
|
|
||||||
app.helpers ::Padrino::Helpers::OutputHelpers
|
|
||||||
app.helpers ::Padrino::Helpers::TagHelpers
|
|
||||||
app.helpers ::Padrino::Helpers::AssetTagHelpers
|
|
||||||
app.helpers ::Padrino::Helpers::FormHelpers
|
|
||||||
app.helpers ::Padrino::Helpers::FormatHelpers
|
|
||||||
app.helpers ::Padrino::Helpers::RenderHelpers
|
|
||||||
app.helpers ::Padrino::Helpers::NumberHelpers
|
|
||||||
# app.helpers ::Padrino::Helpers::TranslationHelpers
|
|
||||||
|
|
||||||
app.helpers Helpers
|
|
||||||
end
|
|
||||||
alias :included :registered
|
|
||||||
end
|
|
||||||
|
|
||||||
# The helpers
|
|
||||||
module Helpers
|
|
||||||
# Output a stylesheet link tag based on the current path
|
|
||||||
#
|
|
||||||
# @param [String] separator How to break up path in parts
|
|
||||||
# @return [String]
|
|
||||||
def auto_stylesheet_link_tag(separator="/")
|
|
||||||
auto_tag(:css, separator) do |path|
|
|
||||||
stylesheet_link_tag path
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Output a javascript tag based on the current path
|
# The helpers
|
||||||
#
|
module Helpers
|
||||||
# @param [String] separator How to break up path in parts
|
# Output a stylesheet link tag based on the current path
|
||||||
# @return [String]
|
#
|
||||||
def auto_javascript_include_tag(separator="/")
|
# @param [String] separator How to break up path in parts
|
||||||
auto_tag(:js, separator) do |path|
|
# @return [String]
|
||||||
javascript_include_tag path
|
def auto_stylesheet_link_tag(separator="/")
|
||||||
end
|
auto_tag(:css, separator) do |path|
|
||||||
end
|
stylesheet_link_tag path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Output a stylesheet link tag based on the current path
|
# Output a javascript tag based on the current path
|
||||||
#
|
#
|
||||||
# @param [Symbol] asset_ext The type of asset
|
# @param [String] separator How to break up path in parts
|
||||||
# @param [String] separator How to break up path in parts
|
# @return [String]
|
||||||
# @param [String] asset_dir Where to look for assets
|
def auto_javascript_include_tag(separator="/")
|
||||||
# @return [void]
|
auto_tag(:js, separator) do |path|
|
||||||
def auto_tag(asset_ext, separator="/", asset_dir=nil)
|
javascript_include_tag path
|
||||||
if asset_dir.nil?
|
end
|
||||||
asset_dir = case asset_ext
|
end
|
||||||
when :js then js_dir
|
|
||||||
when :css then css_dir
|
# Output a stylesheet link tag based on the current path
|
||||||
|
#
|
||||||
|
# @param [Symbol] asset_ext The type of asset
|
||||||
|
# @param [String] separator How to break up path in parts
|
||||||
|
# @param [String] asset_dir Where to look for assets
|
||||||
|
# @return [void]
|
||||||
|
def auto_tag(asset_ext, separator="/", asset_dir=nil)
|
||||||
|
if asset_dir.nil?
|
||||||
|
asset_dir = case asset_ext
|
||||||
|
when :js then js_dir
|
||||||
|
when :css then css_dir
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# If the basename of the request as no extension, assume we are serving a
|
||||||
|
# directory and join index_file to the path.
|
||||||
|
path = full_path(current_path.dup)
|
||||||
|
path = path.sub(%r{^/}, '')
|
||||||
|
path = path.gsub(File.extname(path), ".#{asset_ext}")
|
||||||
|
path = path.gsub("/", separator)
|
||||||
|
|
||||||
|
yield path if sitemap.find_resource_by_path(File.join(asset_dir, path))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate body css classes based on the current path
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
def page_classes
|
||||||
|
path = current_path.dup
|
||||||
|
path << index_file if path.match(%r{/$})
|
||||||
|
path = path.gsub(%r{^/}, '')
|
||||||
|
|
||||||
|
classes = []
|
||||||
|
parts = path.split('.')[0].split('/')
|
||||||
|
parts.each_with_index { |path, i| classes << parts.first(i+1).join('_') }
|
||||||
|
|
||||||
|
classes.join(' ')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the path of a file of a given type
|
||||||
|
#
|
||||||
|
# @param [Symbol] kind The type of file
|
||||||
|
# @param [String] source The path to the file
|
||||||
|
# @return [String]
|
||||||
|
def asset_path(kind, source)
|
||||||
|
return source if source =~ /^http/
|
||||||
|
asset_folder = case kind
|
||||||
|
when :css then css_dir
|
||||||
|
when :js then js_dir
|
||||||
|
when :images then images_dir
|
||||||
|
else kind.to_s
|
||||||
|
end
|
||||||
|
source = source.to_s.gsub(/\s/, '')
|
||||||
|
ignore_extension = (kind == :images) # don't append extension
|
||||||
|
source << ".#{kind}" unless ignore_extension or source =~ /\.#{kind}/
|
||||||
|
result_path = source if source =~ %r{^/} # absolute path
|
||||||
|
result_path ||= asset_url(source, asset_folder)
|
||||||
|
"#{result_path}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_to(*args, &block)
|
||||||
|
url_arg_index = block_given? ? 0 : 1
|
||||||
|
if url = args[url_arg_index]
|
||||||
|
# Only try to work with absolute URLs
|
||||||
|
if url.start_with? '/'
|
||||||
|
resource = sitemap.find_resource_by_path(url)
|
||||||
|
args[url_arg_index] = resource.url if resource
|
||||||
|
end
|
||||||
|
end
|
||||||
|
super(*args, &block)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# If the basename of the request as no extension, assume we are serving a
|
|
||||||
# directory and join index_file to the path.
|
|
||||||
path = full_path(current_path.dup)
|
|
||||||
path = path.sub(%r{^/}, '')
|
|
||||||
path = path.gsub(File.extname(path), ".#{asset_ext}")
|
|
||||||
path = path.gsub("/", separator)
|
|
||||||
|
|
||||||
yield path if sitemap.find_resource_by_path(File.join(asset_dir, path))
|
|
||||||
end
|
|
||||||
|
|
||||||
# Generate body css classes based on the current path
|
|
||||||
#
|
|
||||||
# @return [String]
|
|
||||||
def page_classes
|
|
||||||
path = current_path.dup
|
|
||||||
path << index_file if path.match(%r{/$})
|
|
||||||
path = path.gsub(%r{^/}, '')
|
|
||||||
|
|
||||||
classes = []
|
|
||||||
parts = path.split('.')[0].split('/')
|
|
||||||
parts.each_with_index { |path, i| classes << parts.first(i+1).join('_') }
|
|
||||||
|
|
||||||
classes.join(' ')
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the path of a file of a given type
|
|
||||||
#
|
|
||||||
# @param [Symbol] kind The type of file
|
|
||||||
# @param [String] source The path to the file
|
|
||||||
# @return [String]
|
|
||||||
def asset_path(kind, source)
|
|
||||||
return source if source =~ /^http/
|
|
||||||
asset_folder = case kind
|
|
||||||
when :css then css_dir
|
|
||||||
when :js then js_dir
|
|
||||||
when :images then images_dir
|
|
||||||
else kind.to_s
|
|
||||||
end
|
|
||||||
source = source.to_s.gsub(/\s/, '')
|
|
||||||
ignore_extension = (kind == :images) # don't append extension
|
|
||||||
source << ".#{kind}" unless ignore_extension or source =~ /\.#{kind}/
|
|
||||||
result_path = source if source =~ %r{^/} # absolute path
|
|
||||||
result_path ||= asset_url(source, asset_folder)
|
|
||||||
"#{result_path}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def link_to(*args, &block)
|
|
||||||
url_arg_index = block_given? ? 0 : 1
|
|
||||||
if url = args[url_arg_index]
|
|
||||||
# Only try to work with absolute URLs
|
|
||||||
if url.start_with? '/'
|
|
||||||
resource = sitemap.find_resource_by_path(url)
|
|
||||||
args[url_arg_index] = resource.url if resource
|
|
||||||
end
|
|
||||||
end
|
|
||||||
super(*args, &block)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -28,125 +28,129 @@
|
||||||
# methods to use in your views. Some modify the output on-the-fly. And some
|
# methods to use in your views. Some modify the output on-the-fly. And some
|
||||||
# apply computationally-intensive changes to your final build files.
|
# apply computationally-intensive changes to your final build files.
|
||||||
|
|
||||||
# Using for version parsing
|
|
||||||
require "rubygems"
|
|
||||||
|
|
||||||
# Namespace extensions module
|
# Namespace extensions module
|
||||||
module Middleman::CoreExtensions::Extensions
|
module Middleman
|
||||||
|
module CoreExtensions
|
||||||
|
module Extensions
|
||||||
|
|
||||||
# Register extension
|
# Register extension
|
||||||
class << self
|
class << self
|
||||||
# @private
|
# @private
|
||||||
def included(app)
|
def included(app)
|
||||||
# app.set :default_extensions, []
|
# Using for version parsing
|
||||||
app.define_hook :after_configuration
|
require "rubygems"
|
||||||
app.define_hook :before_configuration
|
|
||||||
app.define_hook :build_config
|
|
||||||
app.define_hook :development_config
|
|
||||||
|
|
||||||
app.extend ClassMethods
|
|
||||||
app.send :include, InstanceMethods
|
|
||||||
app.delegate :configure, :to => :"self.class"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Class methods
|
# app.set :default_extensions, []
|
||||||
module ClassMethods
|
app.define_hook :after_configuration
|
||||||
# Add a callback to run in a specific environment
|
app.define_hook :before_configuration
|
||||||
#
|
app.define_hook :build_config
|
||||||
# @param [String, Symbol] env The environment to run in
|
app.define_hook :development_config
|
||||||
# @return [void]
|
|
||||||
def configure(env, &block)
|
|
||||||
send("#{env}_config", &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Alias `extensions` to access registered extensions
|
|
||||||
#
|
|
||||||
# @return [Array<Module>]
|
|
||||||
def extensions
|
|
||||||
@extensions ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
# Register a new extension
|
|
||||||
#
|
|
||||||
# @param [Module] extension Extension modules to register
|
|
||||||
# @param [Hash] options Per-extension options hash
|
|
||||||
# @return [void]
|
|
||||||
def register(extension, options={}, &block)
|
|
||||||
@extensions ||= []
|
|
||||||
@extensions += [extension]
|
|
||||||
|
|
||||||
extend extension
|
app.extend ClassMethods
|
||||||
if extension.respond_to?(:registered)
|
app.send :include, InstanceMethods
|
||||||
if extension.method(:registered).arity === 1
|
app.delegate :configure, :to => :"self.class"
|
||||||
extension.registered(self, &block)
|
end
|
||||||
else
|
end
|
||||||
extension.registered(self, options, &block)
|
|
||||||
|
# Class methods
|
||||||
|
module ClassMethods
|
||||||
|
# Add a callback to run in a specific environment
|
||||||
|
#
|
||||||
|
# @param [String, Symbol] env The environment to run in
|
||||||
|
# @return [void]
|
||||||
|
def configure(env, &block)
|
||||||
|
send("#{env}_config", &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Alias `extensions` to access registered extensions
|
||||||
|
#
|
||||||
|
# @return [Array<Module>]
|
||||||
|
def extensions
|
||||||
|
@extensions ||= []
|
||||||
|
end
|
||||||
|
|
||||||
|
# Register a new extension
|
||||||
|
#
|
||||||
|
# @param [Module] extension Extension modules to register
|
||||||
|
# @param [Hash] options Per-extension options hash
|
||||||
|
# @return [void]
|
||||||
|
def register(extension, options={}, &block)
|
||||||
|
@extensions ||= []
|
||||||
|
@extensions += [extension]
|
||||||
|
|
||||||
|
extend extension
|
||||||
|
if extension.respond_to?(:registered)
|
||||||
|
if extension.method(:registered).arity === 1
|
||||||
|
extension.registered(self, &block)
|
||||||
|
else
|
||||||
|
extension.registered(self, options, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Instance methods
|
# Instance methods
|
||||||
module InstanceMethods
|
module InstanceMethods
|
||||||
# This method is available in the project's `config.rb`.
|
# This method is available in the project's `config.rb`.
|
||||||
# It takes a underscore-separated symbol, finds the appropriate
|
# It takes a underscore-separated symbol, finds the appropriate
|
||||||
# feature module and includes it.
|
# feature module and includes it.
|
||||||
#
|
#
|
||||||
# activate :lorem
|
# activate :lorem
|
||||||
#
|
#
|
||||||
# @param [Symbol, Module] ext Which extension to activate
|
# @param [Symbol, Module] ext Which extension to activate
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def activate(ext, options={}, &block)
|
def activate(ext, options={}, &block)
|
||||||
# Make :i18n a no-op
|
# Make :i18n a no-op
|
||||||
return if ext == :i18n
|
return if ext == :i18n
|
||||||
|
|
||||||
ext_module = if ext.is_a?(Module)
|
ext_module = if ext.is_a?(Module)
|
||||||
ext
|
ext
|
||||||
else
|
else
|
||||||
::Middleman::Extensions.load(ext.to_sym)
|
::Middleman::Extensions.load(ext.to_sym)
|
||||||
end
|
end
|
||||||
|
|
||||||
if ext_module.nil?
|
if ext_module.nil?
|
||||||
puts "== Unknown Extension: #{ext}"
|
puts "== Unknown Extension: #{ext}"
|
||||||
else
|
else
|
||||||
puts "== Activating: #{ext}" if logging?
|
puts "== Activating: #{ext}" if logging?
|
||||||
self.class.register(ext_module, options, &block)
|
self.class.register(ext_module, options, &block)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Load features before starting server
|
# Load features before starting server
|
||||||
def initialize
|
def initialize
|
||||||
super
|
super
|
||||||
|
|
||||||
self.class.inst = self
|
self.class.inst = self
|
||||||
run_hook :before_configuration
|
run_hook :before_configuration
|
||||||
|
|
||||||
# Search the root of the project for required files
|
# Search the root of the project for required files
|
||||||
$LOAD_PATH.unshift(root)
|
$LOAD_PATH.unshift(root)
|
||||||
|
|
||||||
# Check for and evaluate local configuration
|
# Check for and evaluate local configuration
|
||||||
local_config = File.join(root, "config.rb")
|
local_config = File.join(root, "config.rb")
|
||||||
if File.exists? local_config
|
if File.exists? local_config
|
||||||
puts "== Reading: Local config" if logging?
|
puts "== Reading: Local config" if logging?
|
||||||
instance_eval File.read(local_config), local_config, 1
|
instance_eval File.read(local_config), local_config, 1
|
||||||
end
|
end
|
||||||
|
|
||||||
run_hook :build_config if build?
|
run_hook :build_config if build?
|
||||||
run_hook :development_config if development?
|
run_hook :development_config if development?
|
||||||
|
|
||||||
run_hook :after_configuration
|
run_hook :after_configuration
|
||||||
|
|
||||||
# Add in defaults
|
# Add in defaults
|
||||||
default_extensions.each do |ext|
|
default_extensions.each do |ext|
|
||||||
activate ext
|
activate ext
|
||||||
end
|
end
|
||||||
|
|
||||||
if logging?
|
if logging?
|
||||||
self.class.extensions.each do |ext|
|
self.class.extensions.each do |ext|
|
||||||
puts "== Extension: #{ext}"
|
puts "== Extension: #{ext}"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,35 +1,39 @@
|
||||||
# Load helpers in helpers/
|
# Load helpers in helpers/
|
||||||
module Middleman::CoreExtensions::ExternalHelpers
|
module Middleman
|
||||||
|
module CoreExtensions
|
||||||
|
module ExternalHelpers
|
||||||
|
|
||||||
# Setup extension
|
# Setup extension
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
# once registered
|
# once registered
|
||||||
def registered(app)
|
def registered(app)
|
||||||
# Setup a default helpers paths
|
# Setup a default helpers paths
|
||||||
app.set :helpers_dir, "helpers"
|
app.set :helpers_dir, "helpers"
|
||||||
app.set :helpers_filename_glob, "**/*_helper.rb"
|
app.set :helpers_filename_glob, "**/*_helper.rb"
|
||||||
app.set :helpers_filename_to_module_name_proc, Proc.new { |filename|
|
app.set :helpers_filename_to_module_name_proc, Proc.new { |filename|
|
||||||
basename = File.basename(filename, File.extname(filename))
|
basename = File.basename(filename, File.extname(filename))
|
||||||
basename.camelcase
|
basename.camelcase
|
||||||
}
|
}
|
||||||
|
|
||||||
# After config
|
# After config
|
||||||
app.after_configuration do
|
app.after_configuration do
|
||||||
helpers_path = File.expand_path(helpers_dir, root)
|
helpers_path = File.expand_path(helpers_dir, root)
|
||||||
next unless File.exists?(helpers_path)
|
next unless File.exists?(helpers_path)
|
||||||
|
|
||||||
Dir[File.join(helpers_path, helpers_filename_glob)].each do |filename|
|
Dir[File.join(helpers_path, helpers_filename_glob)].each do |filename|
|
||||||
module_name = helpers_filename_to_module_name_proc.call(filename)
|
module_name = helpers_filename_to_module_name_proc.call(filename)
|
||||||
next unless module_name
|
next unless module_name
|
||||||
|
|
||||||
require filename
|
require filename
|
||||||
next unless Object.const_defined?(module_name.to_sym)
|
next unless Object.const_defined?(module_name.to_sym)
|
||||||
|
|
||||||
helpers Object.const_get(module_name.to_sym)
|
helpers Object.const_get(module_name.to_sym)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
alias :included :registered
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
alias :included :registered
|
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,153 +1,157 @@
|
||||||
require "find"
|
|
||||||
require "middleman-core/watcher"
|
|
||||||
require "set"
|
|
||||||
|
|
||||||
# API for watching file change events
|
# API for watching file change events
|
||||||
module Middleman::CoreExtensions::FileWatcher
|
module Middleman
|
||||||
|
module CoreExtensions
|
||||||
|
module FileWatcher
|
||||||
|
|
||||||
# Setup extension
|
# Setup extension
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
# Once registered
|
# Once registered
|
||||||
def registered(app)
|
def registered(app)
|
||||||
app.extend ClassMethods
|
require "find"
|
||||||
app.send :include, InstanceMethods
|
require "middleman-core/watcher"
|
||||||
|
require "set"
|
||||||
|
|
||||||
|
app.extend ClassMethods
|
||||||
|
app.send :include, InstanceMethods
|
||||||
|
|
||||||
# Before parsing config, load the data/ directory
|
# Before parsing config, load the data/ directory
|
||||||
app.before_configuration do
|
app.before_configuration do
|
||||||
data_path = File.join(self.root, self.data_dir)
|
data_path = File.join(self.root, self.data_dir)
|
||||||
self.files.reload_path(data_path) if File.exists?(data_path)
|
self.files.reload_path(data_path) if File.exists?(data_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
# After config, load everything else
|
||||||
|
app.ready do
|
||||||
|
self.files.reload_path(self.root)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
alias :included :registered
|
||||||
end
|
end
|
||||||
|
|
||||||
# After config, load everything else
|
# Class methods
|
||||||
app.ready do
|
module ClassMethods
|
||||||
self.files.reload_path(self.root)
|
|
||||||
|
# Access the file api
|
||||||
|
# @return [Middleman::CoreExtensions::FileWatcher::API]
|
||||||
|
def files
|
||||||
|
@_files_api ||= API.new
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
alias :included :registered
|
|
||||||
end
|
|
||||||
|
|
||||||
# Class methods
|
# Instance methods
|
||||||
module ClassMethods
|
module InstanceMethods
|
||||||
|
|
||||||
# Access the file api
|
# Access the file api
|
||||||
# @return [Middleman::CoreExtensions::FileWatcher::API]
|
# @return [Middleman::CoreExtensions::FileWatcher::API]
|
||||||
def files
|
def files
|
||||||
@_files_api ||= API.new
|
api = self.class.files
|
||||||
end
|
api.instance ||= self
|
||||||
end
|
api
|
||||||
|
end
|
||||||
# Instance methods
|
|
||||||
module InstanceMethods
|
|
||||||
|
|
||||||
# Access the file api
|
|
||||||
# @return [Middleman::CoreExtensions::FileWatcher::API]
|
|
||||||
def files
|
|
||||||
api = self.class.files
|
|
||||||
api.instance ||= self
|
|
||||||
api
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Core File Change API class
|
|
||||||
class API
|
|
||||||
attr_accessor :instance, :known_paths
|
|
||||||
|
|
||||||
# Initialize api and internal path cache
|
|
||||||
def initialize
|
|
||||||
self.known_paths = Set.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add callback to be run on file change
|
|
||||||
#
|
|
||||||
# @param [nil,Regexp] matcher A Regexp to match the change path against
|
|
||||||
# @return [Array<Proc>]
|
|
||||||
def changed(matcher=nil, &block)
|
|
||||||
@_changed ||= []
|
|
||||||
@_changed << [block, matcher] if block_given?
|
|
||||||
@_changed
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add callback to be run on file deletion
|
|
||||||
#
|
|
||||||
# @param [nil,Regexp] matcher A Regexp to match the deleted path against
|
|
||||||
# @return [Array<Proc>]
|
|
||||||
def deleted(matcher=nil, &block)
|
|
||||||
@_deleted ||= []
|
|
||||||
@_deleted << [block, matcher] if block_given?
|
|
||||||
@_deleted
|
|
||||||
end
|
|
||||||
|
|
||||||
# Notify callbacks that a file changed
|
|
||||||
#
|
|
||||||
# @param [String] path The file that changed
|
|
||||||
# @return [void]
|
|
||||||
def did_change(path)
|
|
||||||
puts "== File Change: #{path}" if instance.logging? && !::Middleman::Watcher.ignore_list.any? { |r| path.match(r) }
|
|
||||||
self.known_paths << path
|
|
||||||
self.run_callbacks(path, :changed)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Notify callbacks that a file was deleted
|
|
||||||
#
|
|
||||||
# @param [String] path The file that was deleted
|
|
||||||
# @return [void]
|
|
||||||
def did_delete(path)
|
|
||||||
puts "== File Deletion: #{path}" if instance.logging? && !::Middleman::Watcher.ignore_list.any? { |r| path.match(r) }
|
|
||||||
self.known_paths.delete(path)
|
|
||||||
self.run_callbacks(path, :deleted)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Manually trigger update events
|
|
||||||
#
|
|
||||||
# @param [String] path The path to reload
|
|
||||||
# @return [void]
|
|
||||||
def reload_path(path)
|
|
||||||
relative_path = path.sub("#{self.instance.root}/", "")
|
|
||||||
subset = self.known_paths.select { |p| p.match(%r{^#{relative_path}}) }
|
|
||||||
|
|
||||||
Find.find(path) do |path|
|
|
||||||
next if File.directory?(path)
|
|
||||||
next if Middleman::Watcher.ignore_list.any? { |r| path.match(r) }
|
|
||||||
relative_path = path.sub("#{self.instance.root}/", "")
|
|
||||||
subset.delete(relative_path)
|
|
||||||
self.did_change(relative_path)
|
|
||||||
end if File.exists?(path)
|
|
||||||
|
|
||||||
subset.each do |removed_path|
|
|
||||||
self.did_delete(removed_path)
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
# Core File Change API class
|
||||||
# Like reload_path, but only triggers events on new files
|
class API
|
||||||
#
|
attr_accessor :instance, :known_paths
|
||||||
# @param [String] path The path to reload
|
|
||||||
# @return [void]
|
|
||||||
def find_new_files(path)
|
|
||||||
relative_path = path.sub("#{self.instance.root}/", "")
|
|
||||||
subset = self.known_paths.select { |p| p.match(%r{^#{relative_path}}) }
|
|
||||||
|
|
||||||
Find.find(path) do |file|
|
|
||||||
next if File.directory?(file)
|
|
||||||
next if Middleman::Watcher.ignore_list.any? { |r| path.match(r) }
|
|
||||||
relative_path = file.sub("#{self.instance.root}/", "")
|
|
||||||
self.did_change(relative_path) unless subset.include?(relative_path)
|
|
||||||
end if File.exists?(path)
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
# Initialize api and internal path cache
|
||||||
# Notify callbacks for a file given an array of callbacks
|
def initialize
|
||||||
#
|
self.known_paths = Set.new
|
||||||
# @param [String] path The file that was changed
|
end
|
||||||
# @param [Symbol] callbacks_name The name of the callbacks method
|
|
||||||
# @return [void]
|
# Add callback to be run on file change
|
||||||
def run_callbacks(path, callbacks_name)
|
#
|
||||||
return if ::Middleman::Watcher.ignore_list.any? { |r| path.match(r) }
|
# @param [nil,Regexp] matcher A Regexp to match the change path against
|
||||||
|
# @return [Array<Proc>]
|
||||||
|
def changed(matcher=nil, &block)
|
||||||
|
@_changed ||= []
|
||||||
|
@_changed << [block, matcher] if block_given?
|
||||||
|
@_changed
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add callback to be run on file deletion
|
||||||
|
#
|
||||||
|
# @param [nil,Regexp] matcher A Regexp to match the deleted path against
|
||||||
|
# @return [Array<Proc>]
|
||||||
|
def deleted(matcher=nil, &block)
|
||||||
|
@_deleted ||= []
|
||||||
|
@_deleted << [block, matcher] if block_given?
|
||||||
|
@_deleted
|
||||||
|
end
|
||||||
|
|
||||||
|
# Notify callbacks that a file changed
|
||||||
|
#
|
||||||
|
# @param [String] path The file that changed
|
||||||
|
# @return [void]
|
||||||
|
def did_change(path)
|
||||||
|
puts "== File Change: #{path}" if instance.logging? && !::Middleman::Watcher.ignore_list.any? { |r| path.match(r) }
|
||||||
|
self.known_paths << path
|
||||||
|
self.run_callbacks(path, :changed)
|
||||||
|
end
|
||||||
|
|
||||||
self.send(callbacks_name).each do |callback, matcher|
|
# Notify callbacks that a file was deleted
|
||||||
next if path.match(%r{^#{self.instance.build_dir}/})
|
#
|
||||||
next unless matcher.nil? || path.match(matcher)
|
# @param [String] path The file that was deleted
|
||||||
self.instance.instance_exec(path, &callback)
|
# @return [void]
|
||||||
|
def did_delete(path)
|
||||||
|
puts "== File Deletion: #{path}" if instance.logging? && !::Middleman::Watcher.ignore_list.any? { |r| path.match(r) }
|
||||||
|
self.known_paths.delete(path)
|
||||||
|
self.run_callbacks(path, :deleted)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Manually trigger update events
|
||||||
|
#
|
||||||
|
# @param [String] path The path to reload
|
||||||
|
# @return [void]
|
||||||
|
def reload_path(path)
|
||||||
|
relative_path = path.sub("#{self.instance.root}/", "")
|
||||||
|
subset = self.known_paths.select { |p| p.match(%r{^#{relative_path}}) }
|
||||||
|
|
||||||
|
Find.find(path) do |path|
|
||||||
|
next if File.directory?(path)
|
||||||
|
next if Middleman::Watcher.ignore_list.any? { |r| path.match(r) }
|
||||||
|
relative_path = path.sub("#{self.instance.root}/", "")
|
||||||
|
subset.delete(relative_path)
|
||||||
|
self.did_change(relative_path)
|
||||||
|
end if File.exists?(path)
|
||||||
|
|
||||||
|
subset.each do |removed_path|
|
||||||
|
self.did_delete(removed_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Like reload_path, but only triggers events on new files
|
||||||
|
#
|
||||||
|
# @param [String] path The path to reload
|
||||||
|
# @return [void]
|
||||||
|
def find_new_files(path)
|
||||||
|
relative_path = path.sub("#{self.instance.root}/", "")
|
||||||
|
subset = self.known_paths.select { |p| p.match(%r{^#{relative_path}}) }
|
||||||
|
|
||||||
|
Find.find(path) do |file|
|
||||||
|
next if File.directory?(file)
|
||||||
|
next if Middleman::Watcher.ignore_list.any? { |r| path.match(r) }
|
||||||
|
relative_path = file.sub("#{self.instance.root}/", "")
|
||||||
|
self.did_change(relative_path) unless subset.include?(relative_path)
|
||||||
|
end if File.exists?(path)
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
# Notify callbacks for a file given an array of callbacks
|
||||||
|
#
|
||||||
|
# @param [String] path The file that was changed
|
||||||
|
# @param [Symbol] callbacks_name The name of the callbacks method
|
||||||
|
# @return [void]
|
||||||
|
def run_callbacks(path, callbacks_name)
|
||||||
|
return if ::Middleman::Watcher.ignore_list.any? { |r| path.match(r) }
|
||||||
|
|
||||||
|
self.send(callbacks_name).each do |callback, matcher|
|
||||||
|
next if path.match(%r{^#{self.instance.build_dir}/})
|
||||||
|
next unless matcher.nil? || path.match(matcher)
|
||||||
|
self.instance.instance_exec(path, &callback)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,204 +5,208 @@ require "yaml"
|
||||||
require "tilt"
|
require "tilt"
|
||||||
|
|
||||||
# Frontmatter namespace
|
# Frontmatter namespace
|
||||||
module Middleman::CoreExtensions::FrontMatter
|
module Middleman
|
||||||
|
module CoreExtensions
|
||||||
|
module FrontMatter
|
||||||
|
|
||||||
# Setup extension
|
# Setup extension
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
# Once registered
|
# Once registered
|
||||||
def registered(app)
|
def registered(app)
|
||||||
app.set :frontmatter_extensions, %w(.htm .html .php)
|
app.set :frontmatter_extensions, %w(.htm .html .php)
|
||||||
app.extend ClassMethods
|
app.extend ClassMethods
|
||||||
app.send :include, InstanceMethods
|
app.send :include, InstanceMethods
|
||||||
app.delegate :frontmatter_changed, :to => :"self.class"
|
app.delegate :frontmatter_changed, :to => :"self.class"
|
||||||
|
|
||||||
::Middleman::Sitemap::Resource.send :include, ResourceInstanceMethods
|
::Middleman::Sitemap::Resource.send :include, ResourceInstanceMethods
|
||||||
end
|
|
||||||
alias :included :registered
|
|
||||||
end
|
|
||||||
|
|
||||||
# Frontmatter class methods
|
|
||||||
module ClassMethods
|
|
||||||
|
|
||||||
# Register callback on frontmatter updates
|
|
||||||
# @param [Regexp] matcher
|
|
||||||
# @return [Array<Array<Proc, Regexp>>]
|
|
||||||
def frontmatter_changed(matcher=nil, &block)
|
|
||||||
@_frontmatter_changed ||= []
|
|
||||||
@_frontmatter_changed << [block, matcher] if block_given?
|
|
||||||
@_frontmatter_changed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module ResourceInstanceMethods
|
|
||||||
|
|
||||||
# This page's frontmatter
|
|
||||||
# @return [Hash]
|
|
||||||
def data
|
|
||||||
app.frontmatter(source_file).first
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# Frontmatter instance methods
|
|
||||||
module InstanceMethods
|
|
||||||
|
|
||||||
# Override init
|
|
||||||
def initialize
|
|
||||||
exts = frontmatter_extensions.join("|").gsub(".", "\.")
|
|
||||||
|
|
||||||
static_path = source_dir.sub(root, "").sub(/^\//, "").sub(/\/$/, "") + "/"
|
|
||||||
|
|
||||||
matcher = %r{#{static_path}.*(#{exts})}
|
|
||||||
|
|
||||||
files.changed matcher do |file|
|
|
||||||
frontmatter_extension.touch_file(file)
|
|
||||||
end
|
|
||||||
|
|
||||||
files.deleted matcher do |file|
|
|
||||||
frontmatter_extension.remove_file(file)
|
|
||||||
end
|
|
||||||
|
|
||||||
sitemap.provides_metadata matcher do |path|
|
|
||||||
fmdata = if self.frontmatter_extension.has_data?(path)
|
|
||||||
self.frontmatter(path)[0]
|
|
||||||
else
|
|
||||||
{}
|
|
||||||
end
|
end
|
||||||
|
alias :included :registered
|
||||||
data = {}
|
|
||||||
%w(layout layout_engine).each do |opt|
|
|
||||||
data[opt.to_sym] = fmdata[opt] if fmdata.has_key?(opt)
|
|
||||||
end
|
|
||||||
|
|
||||||
{ :options => data, :page => fmdata }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Initialize class
|
|
||||||
frontmatter_extension
|
|
||||||
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
# Notify callbacks that the frontmatter changed
|
|
||||||
# @param [String] path
|
|
||||||
# @return [void]
|
|
||||||
def frontmatter_did_change(path)
|
|
||||||
frontmatter_changed.each do |callback, matcher|
|
|
||||||
next if path.match(%r{^#{build_dir}/})
|
|
||||||
next if !matcher.nil? && !path.match(matcher)
|
|
||||||
instance_exec(path, &callback)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the frontmatter object
|
|
||||||
# @return [Middleman::CoreExtensions::FrontMatter::FrontMatter]
|
|
||||||
def frontmatter_extension
|
|
||||||
@_frontmatter_extension ||= FrontMatter.new(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the frontmatter for a given path
|
|
||||||
# @param [String] path
|
|
||||||
# @return [Hash]
|
|
||||||
def frontmatter(path)
|
|
||||||
frontmatter_extension.data(path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Core Frontmatter class
|
# Frontmatter class methods
|
||||||
class FrontMatter
|
module ClassMethods
|
||||||
|
|
||||||
# Initialize frontmatter with current app
|
# Register callback on frontmatter updates
|
||||||
# @param [Middleman::Application] app
|
# @param [Regexp] matcher
|
||||||
def initialize(app)
|
# @return [Array<Array<Proc, Regexp>>]
|
||||||
@app = app
|
def frontmatter_changed(matcher=nil, &block)
|
||||||
@source = File.expand_path(@app.source, @app.root)
|
@_frontmatter_changed ||= []
|
||||||
@local_data = {}
|
@_frontmatter_changed << [block, matcher] if block_given?
|
||||||
|
@_frontmatter_changed
|
||||||
# Setup ignore callback
|
|
||||||
@app.ignore do |path|
|
|
||||||
if p = @app.sitemap.find_resource_by_path(path)
|
|
||||||
!p.proxy? && p.data && p.data["ignored"] == true
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
module ResourceInstanceMethods
|
||||||
|
|
||||||
|
# This page's frontmatter
|
||||||
|
# @return [Hash]
|
||||||
|
def data
|
||||||
|
app.frontmatter(source_file).first
|
||||||
|
end
|
||||||
|
|
||||||
# Whether the frontmatter knows about a path
|
end
|
||||||
# @param [String] path
|
|
||||||
# @return [Boolean]
|
# Frontmatter instance methods
|
||||||
def has_data?(path)
|
module InstanceMethods
|
||||||
@local_data.has_key?(path.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update frontmatter if a file changes
|
# Override init
|
||||||
# @param [String] file
|
def initialize
|
||||||
# @return [void]
|
exts = frontmatter_extensions.join("|").gsub(".", "\.")
|
||||||
def touch_file(file)
|
|
||||||
extension = File.extname(file).sub(/\./, "")
|
|
||||||
return unless ::Tilt.mappings.has_key?(extension)
|
|
||||||
|
|
||||||
file = File.expand_path(file, @app.root)
|
static_path = source_dir.sub(root, "").sub(/^\//, "").sub(/\/$/, "") + "/"
|
||||||
content = File.read(file)
|
|
||||||
|
matcher = %r{#{static_path}.*(#{exts})}
|
||||||
|
|
||||||
result = parse_front_matter(content)
|
files.changed matcher do |file|
|
||||||
|
frontmatter_extension.touch_file(file)
|
||||||
|
end
|
||||||
|
|
||||||
|
files.deleted matcher do |file|
|
||||||
|
frontmatter_extension.remove_file(file)
|
||||||
|
end
|
||||||
|
|
||||||
|
sitemap.provides_metadata matcher do |path|
|
||||||
|
fmdata = if self.frontmatter_extension.has_data?(path)
|
||||||
|
self.frontmatter(path)[0]
|
||||||
|
else
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
%w(layout layout_engine).each do |opt|
|
||||||
|
data[opt.to_sym] = fmdata[opt] if fmdata.has_key?(opt)
|
||||||
|
end
|
||||||
|
|
||||||
|
{ :options => data, :page => fmdata }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Initialize class
|
||||||
|
frontmatter_extension
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
# Notify callbacks that the frontmatter changed
|
||||||
|
# @param [String] path
|
||||||
|
# @return [void]
|
||||||
|
def frontmatter_did_change(path)
|
||||||
|
frontmatter_changed.each do |callback, matcher|
|
||||||
|
next if path.match(%r{^#{build_dir}/})
|
||||||
|
next if !matcher.nil? && !path.match(matcher)
|
||||||
|
instance_exec(path, &callback)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the frontmatter object
|
||||||
|
# @return [Middleman::CoreExtensions::FrontMatter::FrontMatter]
|
||||||
|
def frontmatter_extension
|
||||||
|
@_frontmatter_extension ||= FrontMatter.new(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the frontmatter for a given path
|
||||||
|
# @param [String] path
|
||||||
|
# @return [Hash]
|
||||||
|
def frontmatter(path)
|
||||||
|
frontmatter_extension.data(path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Core Frontmatter class
|
||||||
|
class FrontMatter
|
||||||
|
|
||||||
|
# Initialize frontmatter with current app
|
||||||
|
# @param [Middleman::Application] app
|
||||||
|
def initialize(app)
|
||||||
|
@app = app
|
||||||
|
@source = File.expand_path(@app.source, @app.root)
|
||||||
|
@local_data = {}
|
||||||
|
|
||||||
|
# Setup ignore callback
|
||||||
|
@app.ignore do |path|
|
||||||
|
if p = @app.sitemap.find_resource_by_path(path)
|
||||||
|
!p.proxy? && p.data && p.data["ignored"] == true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Whether the frontmatter knows about a path
|
||||||
|
# @param [String] path
|
||||||
|
# @return [Boolean]
|
||||||
|
def has_data?(path)
|
||||||
|
@local_data.has_key?(path.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update frontmatter if a file changes
|
||||||
|
# @param [String] file
|
||||||
|
# @return [void]
|
||||||
|
def touch_file(file)
|
||||||
|
extension = File.extname(file).sub(/\./, "")
|
||||||
|
return unless ::Tilt.mappings.has_key?(extension)
|
||||||
|
|
||||||
|
file = File.expand_path(file, @app.root)
|
||||||
|
content = File.read(file)
|
||||||
|
|
||||||
|
result = parse_front_matter(content)
|
||||||
|
|
||||||
if result
|
if result
|
||||||
data, content = result
|
data, content = result
|
||||||
|
|
||||||
data = ::Middleman::Util.recursively_enhance(data).freeze
|
data = ::Middleman::Util.recursively_enhance(data).freeze
|
||||||
@local_data[file] = [data, content]
|
@local_data[file] = [data, content]
|
||||||
@app.cache.set([:raw_template, file], result[1])
|
@app.cache.set([:raw_template, file], result[1])
|
||||||
@app.frontmatter_did_change(file)
|
@app.frontmatter_did_change(file)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Update frontmatter if a file is delted
|
|
||||||
# @param [String] file
|
|
||||||
# @return [void]
|
|
||||||
def remove_file(file)
|
|
||||||
file = File.expand_path(file, @app.root)
|
|
||||||
|
|
||||||
if @local_data.has_key?(file)
|
|
||||||
@app.cache.remove(:raw_template, file)
|
|
||||||
@local_data.delete(file)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the frontmatter for a given path
|
|
||||||
# @param [String] path
|
|
||||||
# @return [Hash]
|
|
||||||
def data(path)
|
|
||||||
if @local_data.has_key?(path.to_s)
|
|
||||||
@local_data[path.to_s]
|
|
||||||
else
|
|
||||||
[ ::Middleman::Util.recursively_enhance({}).freeze, nil ]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
# Parse frontmatter out of a string
|
|
||||||
# @param [String] content
|
|
||||||
# @return [Array<Hash, String>]
|
|
||||||
def parse_front_matter(content)
|
|
||||||
yaml_regex = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
|
||||||
if content =~ yaml_regex
|
|
||||||
content = content[($1.size + $2.size)..-1]
|
|
||||||
|
|
||||||
begin
|
|
||||||
data = YAML.load($1)
|
|
||||||
rescue => e
|
|
||||||
puts "YAML Exception: #{e.message}"
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Update frontmatter if a file is delted
|
||||||
|
# @param [String] file
|
||||||
|
# @return [void]
|
||||||
|
def remove_file(file)
|
||||||
|
file = File.expand_path(file, @app.root)
|
||||||
|
|
||||||
|
if @local_data.has_key?(file)
|
||||||
|
@app.cache.remove(:raw_template, file)
|
||||||
|
@local_data.delete(file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the frontmatter for a given path
|
||||||
|
# @param [String] path
|
||||||
|
# @return [Hash]
|
||||||
|
def data(path)
|
||||||
|
if @local_data.has_key?(path.to_s)
|
||||||
|
@local_data[path.to_s]
|
||||||
|
else
|
||||||
|
[ ::Middleman::Util.recursively_enhance({}).freeze, nil ]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# Parse frontmatter out of a string
|
||||||
|
# @param [String] content
|
||||||
|
# @return [Array<Hash, String>]
|
||||||
|
def parse_front_matter(content)
|
||||||
|
yaml_regex = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
||||||
|
if content =~ yaml_regex
|
||||||
|
content = content[($1.size + $2.size)..-1]
|
||||||
|
|
||||||
|
begin
|
||||||
|
data = YAML.load($1)
|
||||||
|
rescue => e
|
||||||
|
puts "YAML Exception: #{e.message}"
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
[data, content]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
[data, content]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,142 +1,146 @@
|
||||||
# i18n Namespace
|
# i18n Namespace
|
||||||
module Middleman::CoreExtensions::I18n
|
module Middleman
|
||||||
|
module CoreExtensions
|
||||||
|
module I18n
|
||||||
|
|
||||||
# Setup extension
|
# Setup extension
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
# Once registerd
|
# Once registerd
|
||||||
def registered(app)
|
def registered(app)
|
||||||
app.set :locales_dir, "locales"
|
app.set :locales_dir, "locales"
|
||||||
|
|
||||||
app.send :include, InstanceMethods
|
app.send :include, InstanceMethods
|
||||||
|
|
||||||
# Needed for helpers as well
|
# Needed for helpers as well
|
||||||
app.after_configuration do
|
app.after_configuration do
|
||||||
# This is for making the tests work - since the tests
|
# This is for making the tests work - since the tests
|
||||||
# don't completely reload middleman, I18n.load_path can get
|
# don't completely reload middleman, I18n.load_path can get
|
||||||
# polluted with paths from other test app directories that don't
|
# polluted with paths from other test app directories that don't
|
||||||
# exist anymore.
|
# exist anymore.
|
||||||
::I18n.load_path.delete_if {|path| path =~ %r{tmp/aruba}}
|
::I18n.load_path.delete_if {|path| path =~ %r{tmp/aruba}}
|
||||||
::I18n.load_path += Dir[File.join(root, locales_dir, "*.yml")]
|
::I18n.load_path += Dir[File.join(root, locales_dir, "*.yml")]
|
||||||
::I18n.reload!
|
::I18n.reload!
|
||||||
end
|
|
||||||
end
|
|
||||||
alias :included :registered
|
|
||||||
end
|
|
||||||
|
|
||||||
class Localizer
|
|
||||||
def initialize(app)
|
|
||||||
@app = app
|
|
||||||
@maps = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup(options)
|
|
||||||
@options = options
|
|
||||||
|
|
||||||
@lang_map = @options[:lang_map] || {}
|
|
||||||
@path = @options[:path] || "/:locale/"
|
|
||||||
@templates_dir = @options[:templates_dir] || "localizable"
|
|
||||||
@mount_at_root = @options.has_key?(:mount_at_root) ? @options[:mount_at_root] : langs.first
|
|
||||||
|
|
||||||
if !@app.build?
|
|
||||||
puts "== Locales: #{langs.join(", ")}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Don't output localizable files
|
|
||||||
@app.ignore File.join(@templates_dir, "**")
|
|
||||||
|
|
||||||
@app.sitemap.provides_metadata_for_path do |url|
|
|
||||||
if d = get_localization_data(url)
|
|
||||||
lang, page_id = d
|
|
||||||
instance_vars = Proc.new {
|
|
||||||
::I18n.locale = lang
|
|
||||||
@lang = lang
|
|
||||||
@page_id = page_id
|
|
||||||
}
|
|
||||||
{ :blocks => [instance_vars] }
|
|
||||||
else
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@app.sitemap.register_resource_list_manipulator(
|
|
||||||
:i18n,
|
|
||||||
@app.i18n
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def langs
|
|
||||||
@options[:langs] || begin
|
|
||||||
Dir[File.join(@app.root, @app.locales_dir, "*.yml")].map { |file|
|
|
||||||
File.basename(file).gsub(".yml", "")
|
|
||||||
}.sort.map(&:to_sym)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_localization_data(path)
|
|
||||||
@_localization_data ||= {}
|
|
||||||
@_localization_data[path]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update the main sitemap resource list
|
|
||||||
# @return [void]
|
|
||||||
def manipulate_resource_list(resources)
|
|
||||||
@_localization_data = {}
|
|
||||||
|
|
||||||
new_resources = []
|
|
||||||
|
|
||||||
resources.each do |resource|
|
|
||||||
next unless File.fnmatch(File.join(@templates_dir, "**"), resource.path)
|
|
||||||
|
|
||||||
page_id = File.basename(resource.path, File.extname(resource.path))
|
|
||||||
|
|
||||||
langs.map do |lang|
|
|
||||||
::I18n.locale = lang
|
|
||||||
|
|
||||||
localized_page_id = ::I18n.t("paths.#{page_id}", :default => page_id)
|
|
||||||
path = resource.path.sub(@templates_dir, "")
|
|
||||||
|
|
||||||
# Build lang path
|
|
||||||
if @mount_at_root == lang
|
|
||||||
prefix = "/"
|
|
||||||
else
|
|
||||||
replacement = @lang_map.has_key?(lang) ? @lang_map[lang] : lang
|
|
||||||
prefix = @path.sub(":locale", replacement.to_s)
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
path = ::Middleman::Util.normalize_path(
|
alias :included :registered
|
||||||
File.join(prefix, path.sub(page_id, localized_page_id))
|
end
|
||||||
|
|
||||||
|
class Localizer
|
||||||
|
def initialize(app)
|
||||||
|
@app = app
|
||||||
|
@maps = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def setup(options)
|
||||||
|
@options = options
|
||||||
|
|
||||||
|
@lang_map = @options[:lang_map] || {}
|
||||||
|
@path = @options[:path] || "/:locale/"
|
||||||
|
@templates_dir = @options[:templates_dir] || "localizable"
|
||||||
|
@mount_at_root = @options.has_key?(:mount_at_root) ? @options[:mount_at_root] : langs.first
|
||||||
|
|
||||||
|
if !@app.build?
|
||||||
|
puts "== Locales: #{langs.join(", ")}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Don't output localizable files
|
||||||
|
@app.ignore File.join(@templates_dir, "**")
|
||||||
|
|
||||||
|
@app.sitemap.provides_metadata_for_path do |url|
|
||||||
|
if d = get_localization_data(url)
|
||||||
|
lang, page_id = d
|
||||||
|
instance_vars = Proc.new {
|
||||||
|
::I18n.locale = lang
|
||||||
|
@lang = lang
|
||||||
|
@page_id = page_id
|
||||||
|
}
|
||||||
|
{ :blocks => [instance_vars] }
|
||||||
|
else
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@app.sitemap.register_resource_list_manipulator(
|
||||||
|
:i18n,
|
||||||
|
@app.i18n
|
||||||
)
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def langs
|
||||||
|
@options[:langs] || begin
|
||||||
|
Dir[File.join(@app.root, @app.locales_dir, "*.yml")].map { |file|
|
||||||
|
File.basename(file).gsub(".yml", "")
|
||||||
|
}.sort.map(&:to_sym)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_localization_data(path)
|
||||||
|
@_localization_data ||= {}
|
||||||
|
@_localization_data[path]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update the main sitemap resource list
|
||||||
|
# @return [void]
|
||||||
|
def manipulate_resource_list(resources)
|
||||||
|
@_localization_data = {}
|
||||||
|
|
||||||
|
new_resources = []
|
||||||
|
|
||||||
|
resources.each do |resource|
|
||||||
|
next unless File.fnmatch(File.join(@templates_dir, "**"), resource.path)
|
||||||
|
|
||||||
|
page_id = File.basename(resource.path, File.extname(resource.path))
|
||||||
|
|
||||||
|
langs.map do |lang|
|
||||||
|
::I18n.locale = lang
|
||||||
|
|
||||||
|
localized_page_id = ::I18n.t("paths.#{page_id}", :default => page_id)
|
||||||
|
path = resource.path.sub(@templates_dir, "")
|
||||||
|
|
||||||
@_localization_data[path] = [lang, path, localized_page_id]
|
# Build lang path
|
||||||
|
if @mount_at_root == lang
|
||||||
|
prefix = "/"
|
||||||
|
else
|
||||||
|
replacement = @lang_map.has_key?(lang) ? @lang_map[lang] : lang
|
||||||
|
prefix = @path.sub(":locale", replacement.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
p = ::Middleman::Sitemap::Resource.new(
|
path = ::Middleman::Util.normalize_path(
|
||||||
@app.sitemap,
|
File.join(prefix, path.sub(page_id, localized_page_id))
|
||||||
path
|
)
|
||||||
)
|
|
||||||
p.proxy_to(resource.path)
|
|
||||||
|
|
||||||
new_resources << p
|
@_localization_data[path] = [lang, path, localized_page_id]
|
||||||
|
|
||||||
|
p = ::Middleman::Sitemap::Resource.new(
|
||||||
|
@app.sitemap,
|
||||||
|
path
|
||||||
|
)
|
||||||
|
p.proxy_to(resource.path)
|
||||||
|
|
||||||
|
new_resources << p
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
resources + new_resources
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
resources + new_resources
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Frontmatter class methods
|
# Frontmatter class methods
|
||||||
module InstanceMethods
|
module InstanceMethods
|
||||||
|
|
||||||
# Initialize the i18n
|
# Initialize the i18n
|
||||||
def i18n
|
def i18n
|
||||||
@_i18n ||= Localizer.new(self)
|
@_i18n ||= Localizer.new(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Main i18n API
|
# Main i18n API
|
||||||
def localize(options={})
|
def localize(options={})
|
||||||
settings.after_configuration do
|
settings.after_configuration do
|
||||||
i18n.setup(options)
|
i18n.setup(options)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -9,359 +9,363 @@ class Tilt::Template
|
||||||
end
|
end
|
||||||
|
|
||||||
# Rendering extension
|
# Rendering extension
|
||||||
module Middleman::CoreExtensions::Rendering
|
module Middleman
|
||||||
|
module CoreExtensions
|
||||||
|
module Rendering
|
||||||
|
|
||||||
# Setup extension
|
# Setup extension
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
# Once registered
|
# Once registered
|
||||||
def registered(app)
|
def registered(app)
|
||||||
# Include methods
|
# Include methods
|
||||||
app.send :include, InstanceMethods
|
app.send :include, InstanceMethods
|
||||||
|
|
||||||
# Activate custom renderers
|
# Activate custom renderers
|
||||||
app.register Middleman::Renderers::ERb
|
app.register Middleman::Renderers::ERb
|
||||||
end
|
end
|
||||||
|
|
||||||
alias :included :registered
|
alias :included :registered
|
||||||
end
|
end
|
||||||
|
|
||||||
# Custom error class for handling
|
# Custom error class for handling
|
||||||
class TemplateNotFound < RuntimeError
|
class TemplateNotFound < RuntimeError
|
||||||
end
|
end
|
||||||
|
|
||||||
# Rendering instance methods
|
# Rendering instance methods
|
||||||
module InstanceMethods
|
module InstanceMethods
|
||||||
|
|
||||||
# Override init to clear cache on file removal
|
# Override init to clear cache on file removal
|
||||||
def initialize
|
def initialize
|
||||||
# Default extension map
|
# Default extension map
|
||||||
@_template_extensions = {
|
@_template_extensions = {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static_path = source_dir.sub(self.root, "").sub(/^\//, "")
|
static_path = source_dir.sub(self.root, "").sub(/^\//, "")
|
||||||
render_regex = static_path.empty? ? // : (%r{^#{static_path + "/"}})
|
render_regex = static_path.empty? ? // : (%r{^#{static_path + "/"}})
|
||||||
|
|
||||||
self.files.changed render_regex do |file|
|
self.files.changed render_regex do |file|
|
||||||
path = File.expand_path(file, self.root)
|
path = File.expand_path(file, self.root)
|
||||||
self.cache.remove(:raw_template, path)
|
self.cache.remove(:raw_template, path)
|
||||||
end
|
end
|
||||||
|
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add or overwrite a default template extension
|
# Add or overwrite a default template extension
|
||||||
#
|
#
|
||||||
# @param [Hash] extension_map
|
# @param [Hash] extension_map
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def template_extensions(extension_map={})
|
def template_extensions(extension_map={})
|
||||||
@_template_extensions.merge!(extension_map)
|
@_template_extensions.merge!(extension_map)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Render a template, with layout, given a path
|
# Render a template, with layout, given a path
|
||||||
#
|
#
|
||||||
# @param [String] path
|
# @param [String] path
|
||||||
# @param [Hash] locs
|
# @param [Hash] locs
|
||||||
# @param [Hash] opts
|
# @param [Hash] opts
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def render_template(path, locs={}, opts={})
|
def render_template(path, locs={}, opts={})
|
||||||
# Detect the remdering engine from the extension
|
# Detect the remdering engine from the extension
|
||||||
extension = File.extname(path)
|
extension = File.extname(path)
|
||||||
engine = extension[1..-1].to_sym
|
engine = extension[1..-1].to_sym
|
||||||
|
|
||||||
# Store last engine for later (could be inside nested renders)
|
# Store last engine for later (could be inside nested renders)
|
||||||
@current_engine, engine_was = engine, @current_engine
|
@current_engine, engine_was = engine, @current_engine
|
||||||
|
|
||||||
# Use a dup of self as a context so that instance variables set within
|
# Use a dup of self as a context so that instance variables set within
|
||||||
# the template don't persist for other templates.
|
# the template don't persist for other templates.
|
||||||
context = self.dup
|
context = self.dup
|
||||||
|
|
||||||
# Store current locs/opts for later
|
# Store current locs/opts for later
|
||||||
@current_locs = locs, @current_opts = opts
|
@current_locs = locs, @current_opts = opts
|
||||||
|
|
||||||
# Keep rendering template until we've used up all extensions. This handles
|
# Keep rendering template until we've used up all extensions. This handles
|
||||||
# cases like `style.css.sass.erb`
|
# cases like `style.css.sass.erb`
|
||||||
while ::Tilt[path]
|
while ::Tilt[path]
|
||||||
content = render_individual_file(path, locs, opts, context)
|
content = render_individual_file(path, locs, opts, context)
|
||||||
path = File.basename(path, File.extname(path))
|
path = File.basename(path, File.extname(path))
|
||||||
cache.set([:raw_template, path], content)
|
cache.set([:raw_template, path], content)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Certain output file types don't use layouts
|
# Certain output file types don't use layouts
|
||||||
needs_layout = !%w(.js .json .css .txt).include?(extension)
|
needs_layout = !%w(.js .json .css .txt).include?(extension)
|
||||||
|
|
||||||
# If we need a layout and have a layout, use it
|
# If we need a layout and have a layout, use it
|
||||||
if needs_layout && layout_path = fetch_layout(engine, opts)
|
if needs_layout && layout_path = fetch_layout(engine, opts)
|
||||||
content = render_individual_file(layout_path, locs, opts, context) { content }
|
content = render_individual_file(layout_path, locs, opts, context) { content }
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return result
|
# Return result
|
||||||
content
|
content
|
||||||
ensure
|
ensure
|
||||||
# Pop all the saved variables from earlier as we may be returning to a
|
# Pop all the saved variables from earlier as we may be returning to a
|
||||||
# previous render (layouts, partials, nested layouts).
|
# previous render (layouts, partials, nested layouts).
|
||||||
@current_engine = engine_was
|
@current_engine = engine_was
|
||||||
@content_blocks = nil
|
@content_blocks = nil
|
||||||
@current_locs = nil
|
@current_locs = nil
|
||||||
@current_opts = nil
|
@current_opts = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sinatra/Padrino compatible render method signature referenced by some view
|
# Sinatra/Padrino compatible render method signature referenced by some view
|
||||||
# helpers. Especially partials.
|
# helpers. Especially partials.
|
||||||
#
|
#
|
||||||
# @param [String, Symbol] engine
|
# @param [String, Symbol] engine
|
||||||
# @param [String, Symbol] data
|
# @param [String, Symbol] data
|
||||||
# @param [Hash] options
|
# @param [Hash] options
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def render(engine, data, options={}, &block)
|
def render(engine, data, options={}, &block)
|
||||||
data = data.to_s
|
data = data.to_s
|
||||||
|
|
||||||
locals = options[:locals]
|
locals = options[:locals]
|
||||||
|
|
||||||
found_partial = false
|
found_partial = false
|
||||||
engine = nil
|
engine = nil
|
||||||
|
|
||||||
# If the path is known to the sitemap
|
# If the path is known to the sitemap
|
||||||
if resource = sitemap.find_resource_by_path(current_path)
|
if resource = sitemap.find_resource_by_path(current_path)
|
||||||
current_dir = File.dirname(resource.source_file)
|
current_dir = File.dirname(resource.source_file)
|
||||||
engine = File.extname(resource.source_file)[1..-1].to_sym
|
engine = File.extname(resource.source_file)[1..-1].to_sym
|
||||||
|
|
||||||
# Look for partials relative to the current path
|
# Look for partials relative to the current path
|
||||||
if current_dir != self.source_dir
|
if current_dir != self.source_dir
|
||||||
relative_dir = File.join(current_dir.sub("#{self.source_dir}/", ""), data)
|
relative_dir = File.join(current_dir.sub("#{self.source_dir}/", ""), data)
|
||||||
|
|
||||||
# Try to use the current engine first
|
# Try to use the current engine first
|
||||||
found_partial, found_engine = resolve_template(relative_dir, :preferred_engine => engine, :try_without_underscore => true)
|
found_partial, found_engine = resolve_template(relative_dir, :preferred_engine => engine, :try_without_underscore => true)
|
||||||
|
|
||||||
# Fall back to any engine available
|
# Fall back to any engine available
|
||||||
|
if !found_partial
|
||||||
|
found_partial, found_engine = resolve_template(relative_dir, :try_without_underscore => true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Look in the root for the partial with the current engine
|
||||||
|
if !found_partial && !engine.nil?
|
||||||
|
found_partial, found_engine = resolve_template(data, :preferred_engine => engine, :try_without_underscore => true)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Look in the root with any engine
|
||||||
if !found_partial
|
if !found_partial
|
||||||
found_partial, found_engine = resolve_template(relative_dir, :try_without_underscore => true)
|
found_partial, found_engine = resolve_template(data, :try_without_underscore => true)
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Look in the root for the partial with the current engine
|
|
||||||
if !found_partial && !engine.nil?
|
|
||||||
found_partial, found_engine = resolve_template(data, :preferred_engine => engine, :try_without_underscore => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Look in the root with any engine
|
|
||||||
if !found_partial
|
|
||||||
found_partial, found_engine = resolve_template(data, :try_without_underscore => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Render the partial if found, otherwide throw exception
|
|
||||||
if found_partial
|
|
||||||
render_individual_file(found_partial, locals, options, self, &block)
|
|
||||||
else
|
|
||||||
raise ::Middleman::CoreExtensions::Rendering::TemplateNotFound, "Could not locate partial: #{data}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Render an on-disk file. Used for everything, including layouts.
|
|
||||||
#
|
|
||||||
# @param [String, Symbol] path
|
|
||||||
# @param [Hash] locs
|
|
||||||
# @param [Hash] opts
|
|
||||||
# @param [Class] context
|
|
||||||
# @return [String]
|
|
||||||
def render_individual_file(path, locs = {}, opts = {}, context = self, &block)
|
|
||||||
path = path.to_s
|
|
||||||
|
|
||||||
# Save current buffere for later
|
|
||||||
@_out_buf, _buf_was = "", @_out_buf
|
|
||||||
|
|
||||||
# Read from disk or cache the contents of the file
|
|
||||||
body = cache.fetch(:raw_template, path) do
|
|
||||||
File.read(path)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Merge per-extension options from config
|
|
||||||
extension = File.extname(path)
|
|
||||||
options = opts.merge(options_for_ext(extension))
|
|
||||||
options[:outvar] ||= '@_out_buf'
|
|
||||||
|
|
||||||
# Read compiled template from disk or cache
|
|
||||||
template = cache.fetch(:compiled_template, options, body) do
|
|
||||||
::Tilt.new(path, 1, options) { body }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Render using Tilt
|
|
||||||
template.render(context, locs, &block)
|
|
||||||
ensure
|
|
||||||
# Reset stored buffer
|
|
||||||
@_out_buf = _buf_was
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get a hash of configuration options for a given file extension, from
|
|
||||||
# config.rb
|
|
||||||
#
|
|
||||||
# @param [String] ext
|
|
||||||
# @return [Hash]
|
|
||||||
def options_for_ext(ext)
|
|
||||||
# Read options for extension from config/Tilt or cache
|
|
||||||
cache.fetch(:options_for_ext, ext) do
|
|
||||||
options = {}
|
|
||||||
|
|
||||||
# Find all the engines which handle this extension in tilt. Look for
|
|
||||||
# config variables of that name and merge it
|
|
||||||
extension_class = ::Tilt[ext]
|
|
||||||
::Tilt.mappings.each do |ext, engines|
|
|
||||||
next unless engines.include? extension_class
|
|
||||||
engine_options = respond_to?(ext.to_sym) ? send(ext.to_sym) : {}
|
|
||||||
options.merge!(engine_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
options
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Find a layout for a given engine
|
|
||||||
#
|
|
||||||
# @param [Symbol] engine
|
|
||||||
# @param [Hash] opts
|
|
||||||
# @return [String]
|
|
||||||
def fetch_layout(engine, opts)
|
|
||||||
# The layout name comes from either the system default or the options
|
|
||||||
local_layout = opts.has_key?(:layout) ? opts[:layout] : layout
|
|
||||||
return false unless local_layout
|
|
||||||
|
|
||||||
# Look for engine-specific options
|
|
||||||
engine_options = respond_to?(engine) ? send(engine) : {}
|
|
||||||
|
|
||||||
# The engine for the layout can be set in options, engine_options or passed
|
|
||||||
# into this method
|
|
||||||
layout_engine = if opts.has_key?(:layout_engine)
|
|
||||||
opts[:layout_engine]
|
|
||||||
elsif engine_options.has_key?(:layout_engine)
|
|
||||||
engine_options[:layout_engine]
|
|
||||||
else
|
|
||||||
engine
|
|
||||||
end
|
|
||||||
|
|
||||||
# Automatic mode
|
|
||||||
if local_layout == :_auto_layout
|
|
||||||
# Look for :layout of any extension
|
|
||||||
# If found, use it. If not, continue
|
|
||||||
locate_layout(:layout, layout_engine) || false
|
|
||||||
else
|
|
||||||
# Look for specific layout
|
|
||||||
# If found, use it. If not, error.
|
|
||||||
if layout_path = locate_layout(local_layout, layout_engine)
|
|
||||||
layout_path
|
|
||||||
else
|
|
||||||
raise ::Middleman::CoreExtensions::Rendering::TemplateNotFound, "Could not locate layout: #{local_layout}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Find a layout on-disk, optionally using a specific engine
|
|
||||||
# @param [String] name
|
|
||||||
# @param [Symbol] preferred_engine
|
|
||||||
# @return [String]
|
|
||||||
def locate_layout(name, preferred_engine=nil)
|
|
||||||
# Whether we've found the layout
|
|
||||||
layout_path = false
|
|
||||||
|
|
||||||
# If we prefer a specific engine
|
|
||||||
if !preferred_engine.nil?
|
|
||||||
# Check root
|
|
||||||
layout_path, layout_engine = resolve_template(name, :preferred_engine => preferred_engine)
|
|
||||||
|
|
||||||
# Check layouts folder
|
|
||||||
if !layout_path
|
|
||||||
layout_path, layout_engine = resolve_template(File.join("layouts", name.to_s), :preferred_engine => preferred_engine)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check root, no preference
|
|
||||||
if !layout_path
|
|
||||||
layout_path, layout_engine = resolve_template(name)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check layouts folder, no preference
|
|
||||||
if !layout_path
|
|
||||||
layout_path, layout_engine = resolve_template(File.join("layouts", name.to_s))
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the path
|
|
||||||
layout_path
|
|
||||||
end
|
|
||||||
|
|
||||||
# Allow layouts to be wrapped in the contents of other layouts
|
|
||||||
# @param [String, Symbol] layout_name
|
|
||||||
# @return [void]
|
|
||||||
def wrap_layout(layout_name, &block)
|
|
||||||
content = capture(&block) if block_given?
|
|
||||||
layout_path = locate_layout(layout_name, current_engine)
|
|
||||||
concat render_individual_file(layout_path, @current_locs || {}, @current_opts || {}, self) { content }
|
|
||||||
end
|
|
||||||
|
|
||||||
# The currently rendering engine
|
|
||||||
# @return [Symbol, nil]
|
|
||||||
def current_engine
|
|
||||||
@current_engine ||= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# Find a template on disk given a output path
|
|
||||||
# @param [String] request_path
|
|
||||||
# @param [Hash] options
|
|
||||||
# @return [Array<String, Symbol>, Boolean]
|
|
||||||
def resolve_template(request_path, options={})
|
|
||||||
# Find the path by searching or using the cache
|
|
||||||
request_path = request_path.to_s
|
|
||||||
cache.fetch(:resolve_template, request_path, options) do
|
|
||||||
relative_path = request_path.sub(%r{^/}, "")
|
|
||||||
on_disk_path = File.expand_path(relative_path, self.source_dir)
|
|
||||||
|
|
||||||
# By default, any engine will do
|
|
||||||
preferred_engine = "*"
|
|
||||||
|
|
||||||
# Unless we're specifically looking for a preferred engine
|
|
||||||
if options.has_key?(:preferred_engine)
|
|
||||||
extension_class = ::Tilt[options[:preferred_engine]]
|
|
||||||
matched_exts = []
|
|
||||||
|
|
||||||
# Get a list of extensions for a preferred engine
|
|
||||||
# TODO: Cache this
|
|
||||||
::Tilt.mappings.each do |ext, engines|
|
|
||||||
next unless engines.include? extension_class
|
|
||||||
matched_exts << ext
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Change the glob to only look for the matched extensions
|
# Render the partial if found, otherwide throw exception
|
||||||
if matched_exts.length > 0
|
if found_partial
|
||||||
preferred_engine = "{" + matched_exts.join(",") + "}"
|
render_individual_file(found_partial, locals, options, self, &block)
|
||||||
else
|
else
|
||||||
return false
|
raise ::Middleman::CoreExtensions::Rendering::TemplateNotFound, "Could not locate partial: #{data}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Look for files that match
|
# Render an on-disk file. Used for everything, including layouts.
|
||||||
path_with_ext = on_disk_path + "." + preferred_engine
|
#
|
||||||
|
# @param [String, Symbol] path
|
||||||
found_path = Dir[path_with_ext].find do |path|
|
# @param [Hash] locs
|
||||||
::Tilt[path]
|
# @param [Hash] opts
|
||||||
|
# @param [Class] context
|
||||||
|
# @return [String]
|
||||||
|
def render_individual_file(path, locs = {}, opts = {}, context = self, &block)
|
||||||
|
path = path.to_s
|
||||||
|
|
||||||
|
# Save current buffere for later
|
||||||
|
@_out_buf, _buf_was = "", @_out_buf
|
||||||
|
|
||||||
|
# Read from disk or cache the contents of the file
|
||||||
|
body = cache.fetch(:raw_template, path) do
|
||||||
|
File.read(path)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Merge per-extension options from config
|
||||||
|
extension = File.extname(path)
|
||||||
|
options = opts.merge(options_for_ext(extension))
|
||||||
|
options[:outvar] ||= '@_out_buf'
|
||||||
|
|
||||||
|
# Read compiled template from disk or cache
|
||||||
|
template = cache.fetch(:compiled_template, options, body) do
|
||||||
|
::Tilt.new(path, 1, options) { body }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Render using Tilt
|
||||||
|
template.render(context, locs, &block)
|
||||||
|
ensure
|
||||||
|
# Reset stored buffer
|
||||||
|
@_out_buf = _buf_was
|
||||||
end
|
end
|
||||||
|
|
||||||
if !found_path && options[:try_without_underscore] &&
|
# Get a hash of configuration options for a given file extension, from
|
||||||
path_no_underscore = path_with_ext.
|
# config.rb
|
||||||
sub(relative_path, relative_path.sub(/^_/, "").
|
#
|
||||||
sub(/\/_/, "/"))
|
# @param [String] ext
|
||||||
found_path = Dir[path_no_underscore].find do |path|
|
# @return [Hash]
|
||||||
::Tilt[path]
|
def options_for_ext(ext)
|
||||||
|
# Read options for extension from config/Tilt or cache
|
||||||
|
cache.fetch(:options_for_ext, ext) do
|
||||||
|
options = {}
|
||||||
|
|
||||||
|
# Find all the engines which handle this extension in tilt. Look for
|
||||||
|
# config variables of that name and merge it
|
||||||
|
extension_class = ::Tilt[ext]
|
||||||
|
::Tilt.mappings.each do |ext, engines|
|
||||||
|
next unless engines.include? extension_class
|
||||||
|
engine_options = respond_to?(ext.to_sym) ? send(ext.to_sym) : {}
|
||||||
|
options.merge!(engine_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
options
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Find a layout for a given engine
|
||||||
|
#
|
||||||
|
# @param [Symbol] engine
|
||||||
|
# @param [Hash] opts
|
||||||
|
# @return [String]
|
||||||
|
def fetch_layout(engine, opts)
|
||||||
|
# The layout name comes from either the system default or the options
|
||||||
|
local_layout = opts.has_key?(:layout) ? opts[:layout] : layout
|
||||||
|
return false unless local_layout
|
||||||
|
|
||||||
|
# Look for engine-specific options
|
||||||
|
engine_options = respond_to?(engine) ? send(engine) : {}
|
||||||
|
|
||||||
|
# The engine for the layout can be set in options, engine_options or passed
|
||||||
|
# into this method
|
||||||
|
layout_engine = if opts.has_key?(:layout_engine)
|
||||||
|
opts[:layout_engine]
|
||||||
|
elsif engine_options.has_key?(:layout_engine)
|
||||||
|
engine_options[:layout_engine]
|
||||||
|
else
|
||||||
|
engine
|
||||||
|
end
|
||||||
|
|
||||||
|
# Automatic mode
|
||||||
|
if local_layout == :_auto_layout
|
||||||
|
# Look for :layout of any extension
|
||||||
|
# If found, use it. If not, continue
|
||||||
|
locate_layout(:layout, layout_engine) || false
|
||||||
|
else
|
||||||
|
# Look for specific layout
|
||||||
|
# If found, use it. If not, error.
|
||||||
|
if layout_path = locate_layout(local_layout, layout_engine)
|
||||||
|
layout_path
|
||||||
|
else
|
||||||
|
raise ::Middleman::CoreExtensions::Rendering::TemplateNotFound, "Could not locate layout: #{local_layout}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find a layout on-disk, optionally using a specific engine
|
||||||
|
# @param [String] name
|
||||||
|
# @param [Symbol] preferred_engine
|
||||||
|
# @return [String]
|
||||||
|
def locate_layout(name, preferred_engine=nil)
|
||||||
|
# Whether we've found the layout
|
||||||
|
layout_path = false
|
||||||
|
|
||||||
|
# If we prefer a specific engine
|
||||||
|
if !preferred_engine.nil?
|
||||||
|
# Check root
|
||||||
|
layout_path, layout_engine = resolve_template(name, :preferred_engine => preferred_engine)
|
||||||
|
|
||||||
# If we found one, return it and the found engine
|
# Check layouts folder
|
||||||
if found_path || (File.exists?(on_disk_path) && !File.directory?(on_disk_path))
|
if !layout_path
|
||||||
engine = found_path ? File.extname(found_path)[1..-1].to_sym : nil
|
layout_path, layout_engine = resolve_template(File.join("layouts", name.to_s), :preferred_engine => preferred_engine)
|
||||||
[ found_path || on_disk_path, engine ]
|
end
|
||||||
else
|
end
|
||||||
false
|
|
||||||
|
# Check root, no preference
|
||||||
|
if !layout_path
|
||||||
|
layout_path, layout_engine = resolve_template(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check layouts folder, no preference
|
||||||
|
if !layout_path
|
||||||
|
layout_path, layout_engine = resolve_template(File.join("layouts", name.to_s))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the path
|
||||||
|
layout_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# Allow layouts to be wrapped in the contents of other layouts
|
||||||
|
# @param [String, Symbol] layout_name
|
||||||
|
# @return [void]
|
||||||
|
def wrap_layout(layout_name, &block)
|
||||||
|
content = capture(&block) if block_given?
|
||||||
|
layout_path = locate_layout(layout_name, current_engine)
|
||||||
|
concat render_individual_file(layout_path, @current_locs || {}, @current_opts || {}, self) { content }
|
||||||
|
end
|
||||||
|
|
||||||
|
# The currently rendering engine
|
||||||
|
# @return [Symbol, nil]
|
||||||
|
def current_engine
|
||||||
|
@current_engine ||= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find a template on disk given a output path
|
||||||
|
# @param [String] request_path
|
||||||
|
# @param [Hash] options
|
||||||
|
# @return [Array<String, Symbol>, Boolean]
|
||||||
|
def resolve_template(request_path, options={})
|
||||||
|
# Find the path by searching or using the cache
|
||||||
|
request_path = request_path.to_s
|
||||||
|
cache.fetch(:resolve_template, request_path, options) do
|
||||||
|
relative_path = request_path.sub(%r{^/}, "")
|
||||||
|
on_disk_path = File.expand_path(relative_path, self.source_dir)
|
||||||
|
|
||||||
|
# By default, any engine will do
|
||||||
|
preferred_engine = "*"
|
||||||
|
|
||||||
|
# Unless we're specifically looking for a preferred engine
|
||||||
|
if options.has_key?(:preferred_engine)
|
||||||
|
extension_class = ::Tilt[options[:preferred_engine]]
|
||||||
|
matched_exts = []
|
||||||
|
|
||||||
|
# Get a list of extensions for a preferred engine
|
||||||
|
# TODO: Cache this
|
||||||
|
::Tilt.mappings.each do |ext, engines|
|
||||||
|
next unless engines.include? extension_class
|
||||||
|
matched_exts << ext
|
||||||
|
end
|
||||||
|
|
||||||
|
# Change the glob to only look for the matched extensions
|
||||||
|
if matched_exts.length > 0
|
||||||
|
preferred_engine = "{" + matched_exts.join(",") + "}"
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Look for files that match
|
||||||
|
path_with_ext = on_disk_path + "." + preferred_engine
|
||||||
|
|
||||||
|
found_path = Dir[path_with_ext].find do |path|
|
||||||
|
::Tilt[path]
|
||||||
|
end
|
||||||
|
|
||||||
|
if !found_path && options[:try_without_underscore] &&
|
||||||
|
path_no_underscore = path_with_ext.
|
||||||
|
sub(relative_path, relative_path.sub(/^_/, "").
|
||||||
|
sub(/\/_/, "/"))
|
||||||
|
found_path = Dir[path_no_underscore].find do |path|
|
||||||
|
::Tilt[path]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# If we found one, return it and the found engine
|
||||||
|
if found_path || (File.exists?(on_disk_path) && !File.directory?(on_disk_path))
|
||||||
|
engine = found_path ? File.extname(found_path)[1..-1].to_sym : nil
|
||||||
|
[ found_path || on_disk_path, engine ]
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,6 +1,7 @@
|
||||||
# Base helper to manipulate asset paths
|
|
||||||
module Middleman
|
module Middleman
|
||||||
module CoreExtensions
|
module CoreExtensions
|
||||||
|
|
||||||
|
# Base helper to manipulate asset paths
|
||||||
module Request
|
module Request
|
||||||
|
|
||||||
# Extension registered
|
# Extension registered
|
||||||
|
|
|
@ -1,88 +1,92 @@
|
||||||
# Routing extension
|
# Routing extension
|
||||||
module Middleman::CoreExtensions::Routing
|
module Middleman
|
||||||
|
module CoreExtensions
|
||||||
|
module Routing
|
||||||
|
|
||||||
# Setup extension
|
# Setup extension
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
# Once registered
|
# Once registered
|
||||||
def registered(app)
|
def registered(app)
|
||||||
# Include methods
|
# Include methods
|
||||||
app.send :include, InstanceMethods
|
app.send :include, InstanceMethods
|
||||||
end
|
end
|
||||||
|
|
||||||
alias :included :registered
|
alias :included :registered
|
||||||
end
|
end
|
||||||
|
|
||||||
# Routing instance methods
|
# Routing instance methods
|
||||||
module InstanceMethods
|
module InstanceMethods
|
||||||
|
|
||||||
# Takes a block which allows many pages to have the same layout
|
# Takes a block which allows many pages to have the same layout
|
||||||
#
|
#
|
||||||
# with_layout :admin do
|
# with_layout :admin do
|
||||||
# page "/admin/"
|
# page "/admin/"
|
||||||
# page "/admin/login.html"
|
# page "/admin/login.html"
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# @param [String, Symbol] layout_name
|
# @param [String, Symbol] layout_name
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def with_layout(layout_name, &block)
|
def with_layout(layout_name, &block)
|
||||||
old_layout = layout
|
old_layout = layout
|
||||||
|
|
||||||
set :layout, layout_name
|
set :layout, layout_name
|
||||||
instance_exec(&block) if block_given?
|
instance_exec(&block) if block_given?
|
||||||
ensure
|
ensure
|
||||||
set :layout, old_layout
|
set :layout, old_layout
|
||||||
end
|
|
||||||
|
|
||||||
# The page method allows the layout to be set on a specific path
|
|
||||||
#
|
|
||||||
# page "/about.html", :layout => false
|
|
||||||
# page "/", :layout => :homepage_layout
|
|
||||||
#
|
|
||||||
# @param [String] url
|
|
||||||
# @param [Hash] opts
|
|
||||||
# @return [void]
|
|
||||||
def page(url, opts={}, &block)
|
|
||||||
a_block = block_given? ? block : nil
|
|
||||||
|
|
||||||
# Default layout
|
|
||||||
opts[:layout] = layout if opts[:layout].nil?
|
|
||||||
|
|
||||||
# If the url is a regexp
|
|
||||||
if url.is_a?(Regexp) || url.include?("*")
|
|
||||||
|
|
||||||
# Use the metadata loop for matching against paths at runtime
|
|
||||||
sitemap.provides_metadata_for_path url do |url|
|
|
||||||
{ :options => opts, :blocks => [a_block] }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return
|
# The page method allows the layout to be set on a specific path
|
||||||
end
|
#
|
||||||
|
# page "/about.html", :layout => false
|
||||||
|
# page "/", :layout => :homepage_layout
|
||||||
|
#
|
||||||
|
# @param [String] url
|
||||||
|
# @param [Hash] opts
|
||||||
|
# @return [void]
|
||||||
|
def page(url, opts={}, &block)
|
||||||
|
a_block = block_given? ? block : nil
|
||||||
|
|
||||||
# Normalized path
|
# Default layout
|
||||||
url = full_path(url)
|
opts[:layout] = layout if opts[:layout].nil?
|
||||||
|
|
||||||
# Setup proxy
|
# If the url is a regexp
|
||||||
if opts.has_key?(:proxy)
|
if url.is_a?(Regexp) || url.include?("*")
|
||||||
proxy(url, opts[:proxy])
|
|
||||||
|
|
||||||
if opts.has_key?(:ignore) && opts[:ignore]
|
# Use the metadata loop for matching against paths at runtime
|
||||||
ignore(opts[:proxy])
|
sitemap.provides_metadata_for_path url do |url|
|
||||||
opts.delete(:ignore)
|
{ :options => opts, :blocks => [a_block] }
|
||||||
end
|
end
|
||||||
|
|
||||||
opts.delete(:proxy)
|
return
|
||||||
else
|
end
|
||||||
if opts.has_key?(:ignore) && opts[:ignore]
|
|
||||||
ignore(url)
|
|
||||||
opts.delete(:ignore)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Setup a metadata matcher for rendering those options
|
# Normalized path
|
||||||
sitemap.provides_metadata_for_path url do |url|
|
url = full_path(url)
|
||||||
{ :options => opts, :blocks => [a_block] }
|
|
||||||
|
# Setup proxy
|
||||||
|
if opts.has_key?(:proxy)
|
||||||
|
proxy(url, opts[:proxy])
|
||||||
|
|
||||||
|
if opts.has_key?(:ignore) && opts[:ignore]
|
||||||
|
ignore(opts[:proxy])
|
||||||
|
opts.delete(:ignore)
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.delete(:proxy)
|
||||||
|
else
|
||||||
|
if opts.has_key?(:ignore) && opts[:ignore]
|
||||||
|
ignore(url)
|
||||||
|
opts.delete(:ignore)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Setup a metadata matcher for rendering those options
|
||||||
|
sitemap.provides_metadata_for_path url do |url|
|
||||||
|
{ :options => opts, :blocks => [a_block] }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,26 +1,31 @@
|
||||||
|
# Require lib
|
||||||
|
require 'rack/showexceptions'
|
||||||
|
|
||||||
# Support rack/showexceptions during development
|
# Support rack/showexceptions during development
|
||||||
module Middleman::CoreExtensions::ShowExceptions
|
module Middleman
|
||||||
|
module CoreExtensions
|
||||||
|
module ShowExceptions
|
||||||
|
|
||||||
# Setup extension
|
# Setup extension
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
# Once registered
|
# Once registered
|
||||||
def registered(app)
|
def registered(app)
|
||||||
# Require lib
|
# When in dev
|
||||||
require 'rack/showexceptions'
|
app.configure :development do
|
||||||
|
# Include middlemare
|
||||||
# When in dev
|
if show_exceptions
|
||||||
app.configure :development do
|
use ::Middleman::CoreExtensions::ShowExceptions::Middleware
|
||||||
# Include middlemare
|
end
|
||||||
if show_exceptions
|
end
|
||||||
use ::Middleman::CoreExtensions::ShowExceptions::Middleware
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Custom exception class
|
||||||
|
# TODO: Style this ourselves
|
||||||
|
class Middleware < ::Rack::ShowExceptions
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Custom exception class
|
|
||||||
# TODO: Style this ourselves
|
|
||||||
class Middleware < ::Rack::ShowExceptions
|
|
||||||
end
|
|
||||||
end
|
end
|
|
@ -1,72 +1,7 @@
|
||||||
module Middleman
|
module Middleman
|
||||||
|
|
||||||
# Backwards compatibility namespace
|
|
||||||
module Features
|
|
||||||
end
|
|
||||||
|
|
||||||
module CoreExtensions
|
|
||||||
# Rack Request
|
|
||||||
autoload :Request, "middleman-core/core_extensions/request"
|
|
||||||
|
|
||||||
# File Change Notifier
|
|
||||||
autoload :FileWatcher, "middleman-core/core_extensions/file_watcher"
|
|
||||||
|
|
||||||
# In-memory Sitemap
|
|
||||||
autoload :Sitemap, "middleman-core/core_extensions/sitemap"
|
|
||||||
|
|
||||||
# Add Builder callbacks
|
|
||||||
autoload :Builder, "middleman-core/core_extensions/builder"
|
|
||||||
|
|
||||||
# Custom Feature API
|
|
||||||
autoload :Extensions, "middleman-core/core_extensions/extensions"
|
|
||||||
|
|
||||||
# Asset Path Pipeline
|
|
||||||
autoload :Assets, "middleman-core/core_extensions/assets"
|
|
||||||
|
|
||||||
# Data looks at the data/ folder for YAML files and makes them available
|
|
||||||
# to dynamic requests.
|
|
||||||
autoload :Data, "middleman-core/core_extensions/data"
|
|
||||||
|
|
||||||
# Parse YAML from templates
|
|
||||||
autoload :FrontMatter, "middleman-core/core_extensions/front_matter"
|
|
||||||
|
|
||||||
# External helpers looks in the helpers/ folder for helper modules
|
|
||||||
autoload :ExternalHelpers, "middleman-core/core_extensions/external_helpers"
|
|
||||||
|
|
||||||
# DefaultHelpers are the built-in dynamic template helpers.
|
|
||||||
autoload :DefaultHelpers, "middleman-core/core_extensions/default_helpers"
|
|
||||||
|
|
||||||
# Extended version of Padrino's rendering
|
|
||||||
autoload :Rendering, "middleman-core/core_extensions/rendering"
|
|
||||||
|
|
||||||
# Pass custom options to views
|
|
||||||
autoload :Routing, "middleman-core/core_extensions/routing"
|
|
||||||
|
|
||||||
# Catch and show exceptions at the Rack level
|
|
||||||
autoload :ShowExceptions, "middleman-core/core_extensions/show_exceptions"
|
|
||||||
|
|
||||||
# i18n
|
|
||||||
autoload :I18n, "middleman-core/core_extensions/i18n"
|
|
||||||
end
|
|
||||||
|
|
||||||
module Extensions
|
module Extensions
|
||||||
# Provide Apache-style index.html files for directories
|
|
||||||
autoload :DirectoryIndexes, "middleman-core/extensions/directory_indexes"
|
|
||||||
|
|
||||||
# Lorem provides a handful of helpful prototyping methods to generate
|
|
||||||
# words, paragraphs, fake images, names and email addresses.
|
|
||||||
autoload :Lorem, "middleman-core/extensions/lorem"
|
|
||||||
|
|
||||||
# AutomaticImageSizes inspects the images used in your dynamic templates
|
|
||||||
# and automatically adds width and height attributes to their HTML
|
|
||||||
# elements.
|
|
||||||
autoload :AutomaticImageSizes, "middleman-core/extensions/automatic_image_sizes"
|
|
||||||
|
|
||||||
# AssetHost allows you to setup multiple domains to host your static
|
|
||||||
# assets. Calls to asset paths in dynamic templates will then rotate
|
|
||||||
# through each of the asset servers to better spread the load.
|
|
||||||
autoload :AssetHost, "middleman-core/extensions/asset_host"
|
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def registered
|
def registered
|
||||||
@_registered ||= {}
|
@_registered ||= {}
|
||||||
|
|
|
@ -51,7 +51,4 @@ module Middleman::Extensions
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
# Register the extension
|
|
||||||
register :directory_indexes, DirectoryIndexes
|
|
||||||
end
|
|
|
@ -192,7 +192,4 @@ module Middleman::Extensions
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Register the extension
|
|
||||||
register :lorem, Lorem
|
|
||||||
end
|
end
|
|
@ -1,78 +1,78 @@
|
||||||
|
require "middleman-core/sitemap/store"
|
||||||
|
require "middleman-core/sitemap/resource"
|
||||||
|
|
||||||
|
require "middleman-core/sitemap/extensions/on_disk"
|
||||||
|
require "middleman-core/sitemap/extensions/proxies"
|
||||||
|
require "middleman-core/sitemap/extensions/ignores"
|
||||||
|
|
||||||
# Core Sitemap Extensions
|
# Core Sitemap Extensions
|
||||||
module Middleman::Sitemap
|
module Middleman
|
||||||
|
|
||||||
autoload :Store, "middleman-core/sitemap/store"
|
module Sitemap
|
||||||
autoload :Resource, "middleman-core/sitemap/resource"
|
|
||||||
|
|
||||||
module Extensions
|
# Setup Extension
|
||||||
autoload :OnDisk, "middleman-core/sitemap/extensions/on_disk"
|
class << self
|
||||||
autoload :Proxies, "middleman-core/sitemap/extensions/proxies"
|
|
||||||
autoload :Ignores, "middleman-core/sitemap/extensions/ignores"
|
|
||||||
autoload :Traversal, "middleman-core/sitemap/extensions/traversal"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Setup Extension
|
|
||||||
class << self
|
|
||||||
|
|
||||||
# Once registered
|
# Once registered
|
||||||
def registered(app)
|
def registered(app)
|
||||||
|
|
||||||
app.register Middleman::Sitemap::Extensions::Proxies
|
app.register Middleman::Sitemap::Extensions::Proxies
|
||||||
app.register Middleman::Sitemap::Extensions::Ignores
|
app.register Middleman::Sitemap::Extensions::Ignores
|
||||||
|
|
||||||
# Setup callbacks which can exclude paths from the sitemap
|
# Setup callbacks which can exclude paths from the sitemap
|
||||||
app.set :ignored_sitemap_matchers, {
|
app.set :ignored_sitemap_matchers, {
|
||||||
# dotfiles and folders in the root
|
# dotfiles and folders in the root
|
||||||
:root_dotfiles => proc { |file, path| file.match(/^\./) },
|
:root_dotfiles => proc { |file, path| file.match(/^\./) },
|
||||||
|
|
||||||
# Files starting with an dot, but not .htaccess
|
# Files starting with an dot, but not .htaccess
|
||||||
:source_dotfiles => proc { |file, path|
|
:source_dotfiles => proc { |file, path|
|
||||||
(file.match(/\/\./) && !file.match(/\/\.htaccess/))
|
(file.match(/\/\./) && !file.match(/\/\.htaccess/))
|
||||||
},
|
},
|
||||||
|
|
||||||
# Files starting with an underscore, but not a double-underscore
|
# Files starting with an underscore, but not a double-underscore
|
||||||
:partials => proc { |file, path| (file.match(/\/_/) && !file.match(/\/__/)) },
|
:partials => proc { |file, path| (file.match(/\/_/) && !file.match(/\/__/)) },
|
||||||
|
|
||||||
:layout => proc { |file, path|
|
:layout => proc { |file, path|
|
||||||
file.match(/^source\/layout\./) || file.match(/^source\/layouts\//)
|
file.match(/^source\/layout\./) || file.match(/^source\/layouts\//)
|
||||||
},
|
},
|
||||||
|
|
||||||
# Files without any output extension (layouts, partials)
|
# Files without any output extension (layouts, partials)
|
||||||
# :extensionless => proc { |file, path| !path.match(/\./) },
|
# :extensionless => proc { |file, path| !path.match(/\./) },
|
||||||
}
|
}
|
||||||
|
|
||||||
# Include instance methods
|
# Include instance methods
|
||||||
app.send :include, InstanceMethods
|
app.send :include, InstanceMethods
|
||||||
|
|
||||||
# Initialize Sitemap
|
# Initialize Sitemap
|
||||||
app.before_configuration do
|
app.before_configuration do
|
||||||
sitemap
|
sitemap
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
alias :included :registered
|
||||||
alias :included :registered
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sitemap instance methods
|
# Sitemap instance methods
|
||||||
module InstanceMethods
|
module InstanceMethods
|
||||||
|
|
||||||
|
# Get the sitemap class instance
|
||||||
|
# @return [Middleman::Sitemap::Store]
|
||||||
|
def sitemap
|
||||||
|
@_sitemap ||= Store.new(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the resource object for the current path
|
||||||
|
# @return [Middleman::Sitemap::Resource]
|
||||||
|
def current_page
|
||||||
|
current_resource
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the resource object for the current path
|
||||||
|
# @return [Middleman::Sitemap::Resource]
|
||||||
|
def current_resource
|
||||||
|
sitemap.find_resource_by_destination_path(current_path)
|
||||||
|
end
|
||||||
|
|
||||||
# Get the sitemap class instance
|
|
||||||
# @return [Middleman::Sitemap::Store]
|
|
||||||
def sitemap
|
|
||||||
@_sitemap ||= Store.new(self)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the resource object for the current path
|
|
||||||
# @return [Middleman::Sitemap::Resource]
|
|
||||||
def current_page
|
|
||||||
current_resource
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the resource object for the current path
|
|
||||||
# @return [Middleman::Sitemap::Resource]
|
|
||||||
def current_resource
|
|
||||||
sitemap.find_resource_by_destination_path(current_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,85 +1,91 @@
|
||||||
module Middleman::Sitemap::Extensions
|
module Middleman
|
||||||
|
|
||||||
|
module Sitemap
|
||||||
|
|
||||||
|
module Extensions
|
||||||
|
|
||||||
module Ignores
|
module Ignores
|
||||||
|
|
||||||
# Setup extension
|
# Setup extension
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
# Once registered
|
# Once registered
|
||||||
def registered(app)
|
def registered(app)
|
||||||
# Include methods
|
# Include methods
|
||||||
app.send :include, InstanceMethods
|
app.send :include, InstanceMethods
|
||||||
|
|
||||||
::Middleman::Sitemap::Resource.send :include, ResourceInstanceMethods
|
::Middleman::Sitemap::Resource.send :include, ResourceInstanceMethods
|
||||||
end
|
|
||||||
|
|
||||||
alias :included :registered
|
|
||||||
end
|
|
||||||
|
|
||||||
# Helpers methods for Resources
|
|
||||||
module ResourceInstanceMethods
|
|
||||||
|
|
||||||
# Whether the Resource is ignored
|
|
||||||
# @return [Boolean]
|
|
||||||
def ignored?
|
|
||||||
@app.ignore_manager.ignored?(path) ||
|
|
||||||
(!proxy? &&
|
|
||||||
@app.ignore_manager.ignored?(source_file.sub("#{@app.source_dir}/", ""))
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Ignore-related instance methods
|
|
||||||
module InstanceMethods
|
|
||||||
def ignore_manager
|
|
||||||
@_ignore_manager ||= IgnoreManager.new(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Ignore a path or add an ignore callback
|
|
||||||
# @param [String, Regexp] path Path glob expression, or path regex
|
|
||||||
# @return [void]
|
|
||||||
def ignore(path=nil, &block)
|
|
||||||
ignore_manager.ignore(path, &block)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Class to handle managing ignores
|
|
||||||
class IgnoreManager
|
|
||||||
def initialize(app)
|
|
||||||
@app = app
|
|
||||||
|
|
||||||
# Array of callbacks which can ass ignored
|
|
||||||
@ignored_callbacks = []
|
|
||||||
end
|
|
||||||
|
|
||||||
# Ignore a path or add an ignore callback
|
|
||||||
# @param [String, Regexp] path Path glob expression, or path regex
|
|
||||||
# @return [void]
|
|
||||||
def ignore(path=nil, &block)
|
|
||||||
original_callback_size = @ignored_callbacks.size
|
|
||||||
|
|
||||||
if path.is_a? Regexp
|
|
||||||
@ignored_callbacks << Proc.new {|p| p =~ path }
|
|
||||||
elsif path.is_a? String
|
|
||||||
path_clean = ::Middleman::Util.normalize_path(path)
|
|
||||||
if path_clean.include?("*") # It's a glob
|
|
||||||
@ignored_callbacks << Proc.new {|p| File.fnmatch(path_clean, p) }
|
|
||||||
else
|
|
||||||
# Add a specific-path ignore unless that path is already covered
|
|
||||||
@ignored_callbacks << Proc.new {|p| p == path_clean } unless ignored?(path_clean)
|
|
||||||
end
|
end
|
||||||
elsif block_given?
|
|
||||||
@ignored_callbacks << block
|
alias :included :registered
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
# Helpers methods for Resources
|
||||||
|
module ResourceInstanceMethods
|
||||||
|
|
||||||
|
# Whether the Resource is ignored
|
||||||
|
# @return [Boolean]
|
||||||
|
def ignored?
|
||||||
|
@app.ignore_manager.ignored?(path) ||
|
||||||
|
(!proxy? &&
|
||||||
|
@app.ignore_manager.ignored?(source_file.sub("#{@app.source_dir}/", ""))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Ignore-related instance methods
|
||||||
|
module InstanceMethods
|
||||||
|
def ignore_manager
|
||||||
|
@_ignore_manager ||= IgnoreManager.new(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Ignore a path or add an ignore callback
|
||||||
|
# @param [String, Regexp] path Path glob expression, or path regex
|
||||||
|
# @return [void]
|
||||||
|
def ignore(path=nil, &block)
|
||||||
|
ignore_manager.ignore(path, &block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Class to handle managing ignores
|
||||||
|
class IgnoreManager
|
||||||
|
def initialize(app)
|
||||||
|
@app = app
|
||||||
|
|
||||||
# Whether a path is ignored
|
# Array of callbacks which can ass ignored
|
||||||
# @param [String] path
|
@ignored_callbacks = []
|
||||||
# @return [Boolean]
|
end
|
||||||
def ignored?(path)
|
|
||||||
path_clean = ::Middleman::Util.normalize_path(path)
|
# Ignore a path or add an ignore callback
|
||||||
@ignored_callbacks.any? { |b| b.call(path_clean) }
|
# @param [String, Regexp] path Path glob expression, or path regex
|
||||||
|
# @return [void]
|
||||||
|
def ignore(path=nil, &block)
|
||||||
|
original_callback_size = @ignored_callbacks.size
|
||||||
|
|
||||||
|
if path.is_a? Regexp
|
||||||
|
@ignored_callbacks << Proc.new {|p| p =~ path }
|
||||||
|
elsif path.is_a? String
|
||||||
|
path_clean = ::Middleman::Util.normalize_path(path)
|
||||||
|
if path_clean.include?("*") # It's a glob
|
||||||
|
@ignored_callbacks << Proc.new {|p| File.fnmatch(path_clean, p) }
|
||||||
|
else
|
||||||
|
# Add a specific-path ignore unless that path is already covered
|
||||||
|
@ignored_callbacks << Proc.new {|p| p == path_clean } unless ignored?(path_clean)
|
||||||
|
end
|
||||||
|
elsif block_given?
|
||||||
|
@ignored_callbacks << block
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Whether a path is ignored
|
||||||
|
# @param [String] path
|
||||||
|
# @return [Boolean]
|
||||||
|
def ignored?(path)
|
||||||
|
path_clean = ::Middleman::Util.normalize_path(path)
|
||||||
|
@ignored_callbacks.any? { |b| b.call(path_clean) }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,77 +1,84 @@
|
||||||
require 'set'
|
require 'set'
|
||||||
|
|
||||||
module Middleman::Sitemap::Extensions
|
module Middleman
|
||||||
class OnDisk
|
|
||||||
|
module Sitemap
|
||||||
attr_accessor :sitemap
|
|
||||||
attr_accessor :waiting_for_ready
|
|
||||||
|
|
||||||
def initialize(sitemap)
|
|
||||||
@sitemap = sitemap
|
|
||||||
@app = @sitemap.app
|
|
||||||
|
|
||||||
@file_paths_on_disk = Set.new
|
|
||||||
|
|
||||||
scoped_self = self
|
|
||||||
@waiting_for_ready = true
|
|
||||||
|
|
||||||
# Register file change callback
|
|
||||||
@app.files.changed do |file|
|
|
||||||
scoped_self.touch_file(file, !scoped_self.waiting_for_ready)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Register file delete callback
|
|
||||||
@app.files.deleted do |file|
|
|
||||||
scoped_self.remove_file(file, !scoped_self.waiting_for_ready)
|
|
||||||
end
|
|
||||||
|
|
||||||
@app.ready do
|
|
||||||
scoped_self.waiting_for_ready = false
|
|
||||||
scoped_self.sitemap.rebuild_resource_list!(:on_disk_ready)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update or add an on-disk file path
|
|
||||||
# @param [String] file
|
|
||||||
# @return [Boolean]
|
|
||||||
def touch_file(file, rebuild=true)
|
|
||||||
return false if file == @app.source_dir || File.directory?(file)
|
|
||||||
|
|
||||||
path = @sitemap.file_to_path(file)
|
|
||||||
return false unless path
|
|
||||||
|
|
||||||
ignored = @app.ignored_sitemap_matchers.any? do |name, callback|
|
|
||||||
callback.call(file, path)
|
|
||||||
end
|
|
||||||
|
|
||||||
@file_paths_on_disk << file unless ignored
|
|
||||||
|
|
||||||
# Rebuild the sitemap any time a file is touched
|
|
||||||
# in case one of the other manipulators
|
|
||||||
# (like asset_hash) cares about the contents of this file,
|
|
||||||
# whether or not it belongs in the sitemap (like a partial)
|
|
||||||
@sitemap.rebuild_resource_list!(:touched_file) if rebuild
|
|
||||||
end
|
|
||||||
|
|
||||||
# Remove a file from the store
|
module Extensions
|
||||||
# @param [String] file
|
|
||||||
# @return [void]
|
class OnDisk
|
||||||
def remove_file(file, rebuild=true)
|
|
||||||
if @file_paths_on_disk.delete?(file)
|
attr_accessor :sitemap
|
||||||
@sitemap.rebuild_resource_list!(:removed_file) if rebuild
|
attr_accessor :waiting_for_ready
|
||||||
end
|
|
||||||
end
|
def initialize(sitemap)
|
||||||
|
@sitemap = sitemap
|
||||||
|
@app = @sitemap.app
|
||||||
|
|
||||||
|
@file_paths_on_disk = Set.new
|
||||||
|
|
||||||
|
scoped_self = self
|
||||||
|
@waiting_for_ready = true
|
||||||
|
|
||||||
|
# Register file change callback
|
||||||
|
@app.files.changed do |file|
|
||||||
|
scoped_self.touch_file(file, !scoped_self.waiting_for_ready)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Register file delete callback
|
||||||
|
@app.files.deleted do |file|
|
||||||
|
scoped_self.remove_file(file, !scoped_self.waiting_for_ready)
|
||||||
|
end
|
||||||
|
|
||||||
|
@app.ready do
|
||||||
|
scoped_self.waiting_for_ready = false
|
||||||
|
scoped_self.sitemap.rebuild_resource_list!(:on_disk_ready)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update or add an on-disk file path
|
||||||
|
# @param [String] file
|
||||||
|
# @return [Boolean]
|
||||||
|
def touch_file(file, rebuild=true)
|
||||||
|
return false if file == @app.source_dir || File.directory?(file)
|
||||||
|
|
||||||
|
path = @sitemap.file_to_path(file)
|
||||||
|
return false unless path
|
||||||
|
|
||||||
|
ignored = @app.ignored_sitemap_matchers.any? do |name, callback|
|
||||||
|
callback.call(file, path)
|
||||||
|
end
|
||||||
|
|
||||||
|
@file_paths_on_disk << file unless ignored
|
||||||
|
|
||||||
|
# Rebuild the sitemap any time a file is touched
|
||||||
|
# in case one of the other manipulators
|
||||||
|
# (like asset_hash) cares about the contents of this file,
|
||||||
|
# whether or not it belongs in the sitemap (like a partial)
|
||||||
|
@sitemap.rebuild_resource_list!(:touched_file) if rebuild
|
||||||
|
end
|
||||||
|
|
||||||
|
# Remove a file from the store
|
||||||
|
# @param [String] file
|
||||||
|
# @return [void]
|
||||||
|
def remove_file(file, rebuild=true)
|
||||||
|
if @file_paths_on_disk.delete?(file)
|
||||||
|
@sitemap.rebuild_resource_list!(:removed_file) if rebuild
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Update the main sitemap resource list
|
# Update the main sitemap resource list
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def manipulate_resource_list(resources)
|
def manipulate_resource_list(resources)
|
||||||
resources + @file_paths_on_disk.map do |file|
|
resources + @file_paths_on_disk.map do |file|
|
||||||
::Middleman::Sitemap::Resource.new(
|
::Middleman::Sitemap::Resource.new(
|
||||||
@sitemap,
|
@sitemap,
|
||||||
@sitemap.file_to_path(file),
|
@sitemap.file_to_path(file),
|
||||||
File.expand_path(file, @app.root)
|
File.expand_path(file, @app.root)
|
||||||
)
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,99 +1,104 @@
|
||||||
module Middleman::Sitemap::Extensions
|
module Middleman
|
||||||
|
|
||||||
module Proxies
|
|
||||||
|
|
||||||
# Setup extension
|
|
||||||
class << self
|
|
||||||
|
|
||||||
# Once registered
|
|
||||||
def registered(app)
|
|
||||||
::Middleman::Sitemap::Resource.send :include, ResourceInstanceMethods
|
|
||||||
|
|
||||||
# Include methods
|
|
||||||
app.send :include, InstanceMethods
|
|
||||||
end
|
|
||||||
|
|
||||||
alias :included :registered
|
|
||||||
end
|
|
||||||
|
|
||||||
module ResourceInstanceMethods
|
|
||||||
# Whether this page is a proxy
|
|
||||||
# @return [Boolean]
|
|
||||||
def proxy?
|
|
||||||
!!@proxied_to
|
|
||||||
end
|
|
||||||
|
|
||||||
# Set this page to proxy to a target path
|
|
||||||
# @param [String] target
|
|
||||||
# @return [void]
|
|
||||||
def proxy_to(target)
|
|
||||||
@proxied_to = target
|
|
||||||
end
|
|
||||||
|
|
||||||
# The path of the page this page is proxied to, or nil if it's not proxied.
|
|
||||||
# @return [String]
|
|
||||||
def proxied_to
|
|
||||||
@proxied_to
|
|
||||||
end
|
|
||||||
|
|
||||||
# Whether this page has a template file
|
|
||||||
# @return [Boolean]
|
|
||||||
def template?
|
|
||||||
if proxy?
|
|
||||||
store.find_resource_by_path(proxied_to).template?
|
|
||||||
else
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_source_file
|
|
||||||
if proxy?
|
|
||||||
proxy_resource = store.find_resource_by_path(proxied_to)
|
|
||||||
raise "Path #{path} proxies to unknown file #{proxied_to}" unless proxy_resource
|
|
||||||
proxy_resource.source_file
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module InstanceMethods
|
|
||||||
def proxy_manager
|
|
||||||
@_proxy_manager ||= ProxyManager.new(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def proxy(*args)
|
|
||||||
proxy_manager.proxy(*args)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class ProxyManager
|
|
||||||
def initialize(app)
|
|
||||||
@app = app
|
|
||||||
|
|
||||||
@proxy_paths = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Setup a proxy from a path to a target
|
|
||||||
# @param [String] path
|
|
||||||
# @param [String] target
|
|
||||||
# @return [void]
|
|
||||||
def proxy(path, target)
|
|
||||||
@proxy_paths[::Middleman::Util.normalize_path(path)] = ::Middleman::Util.normalize_path(target)
|
|
||||||
@app.sitemap.rebuild_resource_list!(:added_proxy)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update the main sitemap resource list
|
module Sitemap
|
||||||
# @return [void]
|
|
||||||
def manipulate_resource_list(resources)
|
module Extensions
|
||||||
resources + @proxy_paths.map do |key, value|
|
|
||||||
p = ::Middleman::Sitemap::Resource.new(
|
module Proxies
|
||||||
@app.sitemap,
|
|
||||||
key
|
# Setup extension
|
||||||
)
|
class << self
|
||||||
p.proxy_to(value)
|
|
||||||
p
|
# Once registered
|
||||||
|
def registered(app)
|
||||||
|
::Middleman::Sitemap::Resource.send :include, ResourceInstanceMethods
|
||||||
|
|
||||||
|
# Include methods
|
||||||
|
app.send :include, InstanceMethods
|
||||||
|
end
|
||||||
|
|
||||||
|
alias :included :registered
|
||||||
|
end
|
||||||
|
|
||||||
|
module ResourceInstanceMethods
|
||||||
|
# Whether this page is a proxy
|
||||||
|
# @return [Boolean]
|
||||||
|
def proxy?
|
||||||
|
!!@proxied_to
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set this page to proxy to a target path
|
||||||
|
# @param [String] target
|
||||||
|
# @return [void]
|
||||||
|
def proxy_to(target)
|
||||||
|
@proxied_to = target
|
||||||
|
end
|
||||||
|
|
||||||
|
# The path of the page this page is proxied to, or nil if it's not proxied.
|
||||||
|
# @return [String]
|
||||||
|
def proxied_to
|
||||||
|
@proxied_to
|
||||||
|
end
|
||||||
|
|
||||||
|
# Whether this page has a template file
|
||||||
|
# @return [Boolean]
|
||||||
|
def template?
|
||||||
|
if proxy?
|
||||||
|
store.find_resource_by_path(proxied_to).template?
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_source_file
|
||||||
|
if proxy?
|
||||||
|
proxy_resource = store.find_resource_by_path(proxied_to)
|
||||||
|
raise "Path #{path} proxies to unknown file #{proxied_to}" unless proxy_resource
|
||||||
|
proxy_resource.source_file
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module InstanceMethods
|
||||||
|
def proxy_manager
|
||||||
|
@_proxy_manager ||= ProxyManager.new(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
def proxy(*args)
|
||||||
|
proxy_manager.proxy(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ProxyManager
|
||||||
|
def initialize(app)
|
||||||
|
@app = app
|
||||||
|
|
||||||
|
@proxy_paths = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Setup a proxy from a path to a target
|
||||||
|
# @param [String] path
|
||||||
|
# @param [String] target
|
||||||
|
# @return [void]
|
||||||
|
def proxy(path, target)
|
||||||
|
@proxy_paths[::Middleman::Util.normalize_path(path)] = ::Middleman::Util.normalize_path(target)
|
||||||
|
@app.sitemap.rebuild_resource_list!(:added_proxy)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update the main sitemap resource list
|
||||||
|
# @return [void]
|
||||||
|
def manipulate_resource_list(resources)
|
||||||
|
resources + @proxy_paths.map do |key, value|
|
||||||
|
p = ::Middleman::Sitemap::Resource.new(
|
||||||
|
@app.sitemap,
|
||||||
|
key
|
||||||
|
)
|
||||||
|
p.proxy_to(value)
|
||||||
|
p
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
|
|
@ -1,77 +1,84 @@
|
||||||
module Middleman::Sitemap::Extensions
|
module Middleman
|
||||||
module Traversal
|
|
||||||
# This resource's parent resource
|
|
||||||
# @return [Middleman::Sitemap::Resource, nil]
|
|
||||||
def parent
|
|
||||||
parts = path.split("/")
|
|
||||||
parts.pop if path.include?(app.index_file)
|
|
||||||
|
|
||||||
return nil if parts.length < 1
|
|
||||||
|
|
||||||
parts.pop
|
|
||||||
parts << app.index_file
|
|
||||||
|
|
||||||
parent_path = "/" + parts.join("/")
|
|
||||||
|
|
||||||
store.find_resource_by_destination_path(parent_path)
|
|
||||||
end
|
|
||||||
|
|
||||||
# This resource's child resources
|
module Sitemap
|
||||||
# @return [Array<Middleman::Sitemap::Resource>]
|
|
||||||
def children
|
module Extensions
|
||||||
return [] unless directory_index?
|
|
||||||
|
module Traversal
|
||||||
|
# This resource's parent resource
|
||||||
|
# @return [Middleman::Sitemap::Resource, nil]
|
||||||
|
def parent
|
||||||
|
parts = path.split("/")
|
||||||
|
parts.pop if path.include?(app.index_file)
|
||||||
|
|
||||||
|
return nil if parts.length < 1
|
||||||
|
|
||||||
|
parts.pop
|
||||||
|
parts << app.index_file
|
||||||
|
|
||||||
|
parent_path = "/" + parts.join("/")
|
||||||
|
|
||||||
|
store.find_resource_by_destination_path(parent_path)
|
||||||
|
end
|
||||||
|
|
||||||
if eponymous_directory?
|
# This resource's child resources
|
||||||
base_path = eponymous_directory_path
|
# @return [Array<Middleman::Sitemap::Resource>]
|
||||||
prefix = %r|^#{base_path.sub("/", "\\/")}|
|
def children
|
||||||
else
|
return [] unless directory_index?
|
||||||
base_path = path.sub("#{app.index_file}", "")
|
|
||||||
prefix = %r|^#{base_path.sub("/", "\\/")}|
|
|
||||||
end
|
|
||||||
|
|
||||||
store.resources.select do |sub_resource|
|
if eponymous_directory?
|
||||||
if sub_resource.path == self.path || sub_resource.path !~ prefix
|
base_path = eponymous_directory_path
|
||||||
false
|
prefix = %r|^#{base_path.sub("/", "\\/")}|
|
||||||
else
|
|
||||||
inner_path = sub_resource.path.sub(prefix, "")
|
|
||||||
parts = inner_path.split("/")
|
|
||||||
if parts.length == 1
|
|
||||||
true
|
|
||||||
elsif parts.length == 2
|
|
||||||
parts.last == app.index_file
|
|
||||||
else
|
else
|
||||||
false
|
base_path = path.sub("#{app.index_file}", "")
|
||||||
|
prefix = %r|^#{base_path.sub("/", "\\/")}|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
store.resources.select do |sub_resource|
|
||||||
|
if sub_resource.path == self.path || sub_resource.path !~ prefix
|
||||||
|
false
|
||||||
|
else
|
||||||
|
inner_path = sub_resource.path.sub(prefix, "")
|
||||||
|
parts = inner_path.split("/")
|
||||||
|
if parts.length == 1
|
||||||
|
true
|
||||||
|
elsif parts.length == 2
|
||||||
|
parts.last == app.index_file
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This resource's sibling resources
|
||||||
|
# @return [Array<Middleman::Sitemap::Resource>]
|
||||||
|
def siblings
|
||||||
|
return [] unless parent
|
||||||
|
parent.children.reject { |p| p == self }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Whether this resource either a directory index, or has the same name as an existing directory in the source
|
||||||
|
# @return [Boolean]
|
||||||
|
def directory_index?
|
||||||
|
path.include?(app.index_file) || path =~ /\/$/ || eponymous_directory?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Whether the resource has the same name as a directory in the source
|
||||||
|
# (e.g., if the resource is named 'gallery.html' and a path exists named 'gallery/', this would return true)
|
||||||
|
# @return [Boolean]
|
||||||
|
def eponymous_directory?
|
||||||
|
full_path = File.join(app.source_dir, eponymous_directory_path)
|
||||||
|
!!(File.exists?(full_path) && File.directory?(full_path))
|
||||||
|
end
|
||||||
|
|
||||||
|
# The path for this resource if it were a directory, and not a file
|
||||||
|
# (e.g., for 'gallery.html' this would return 'gallery/')
|
||||||
|
# @return [String]
|
||||||
|
def eponymous_directory_path
|
||||||
|
path.sub(ext, '/').sub(/\/$/, "") + "/"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# This resource's sibling resources
|
|
||||||
# @return [Array<Middleman::Sitemap::Resource>]
|
|
||||||
def siblings
|
|
||||||
return [] unless parent
|
|
||||||
parent.children.reject { |p| p == self }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Whether this resource either a directory index, or has the same name as an existing directory in the source
|
|
||||||
# @return [Boolean]
|
|
||||||
def directory_index?
|
|
||||||
path.include?(app.index_file) || path =~ /\/$/ || eponymous_directory?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Whether the resource has the same name as a directory in the source
|
|
||||||
# (e.g., if the resource is named 'gallery.html' and a path exists named 'gallery/', this would return true)
|
|
||||||
# @return [Boolean]
|
|
||||||
def eponymous_directory?
|
|
||||||
full_path = File.join(app.source_dir, eponymous_directory_path)
|
|
||||||
!!(File.exists?(full_path) && File.directory?(full_path))
|
|
||||||
end
|
|
||||||
|
|
||||||
# The path for this resource if it were a directory, and not a file
|
|
||||||
# (e.g., for 'gallery.html' this would return 'gallery/')
|
|
||||||
# @return [String]
|
|
||||||
def eponymous_directory_path
|
|
||||||
path.sub(ext, '/').sub(/\/$/, "") + "/"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,148 +1,153 @@
|
||||||
# Sitemap namespace
|
require "middleman-core/sitemap/extensions/traversal"
|
||||||
module Middleman::Sitemap
|
|
||||||
|
module Middleman
|
||||||
|
|
||||||
|
# Sitemap namespace
|
||||||
|
module Sitemap
|
||||||
|
|
||||||
# Sitemap Resource class
|
# Sitemap Resource class
|
||||||
class Resource
|
class Resource
|
||||||
include Middleman::Sitemap::Extensions::Traversal
|
include Middleman::Sitemap::Extensions::Traversal
|
||||||
|
|
||||||
# @return [Middleman::Application]
|
# @return [Middleman::Application]
|
||||||
attr_reader :app
|
attr_reader :app
|
||||||
|
|
||||||
# @return [Middleman::Sitemap::Store]
|
# @return [Middleman::Sitemap::Store]
|
||||||
attr_reader :store
|
attr_reader :store
|
||||||
|
|
||||||
# The source path of this resource (relative to the source directory,
|
# The source path of this resource (relative to the source directory,
|
||||||
# without template extensions)
|
# without template extensions)
|
||||||
# @return [String]
|
# @return [String]
|
||||||
attr_reader :path
|
attr_reader :path
|
||||||
|
|
||||||
# Set the on-disk source file for this resource
|
# Set the on-disk source file for this resource
|
||||||
# @return [String]
|
# @return [String]
|
||||||
# attr_reader :source_file
|
# attr_reader :source_file
|
||||||
|
|
||||||
def source_file
|
def source_file
|
||||||
@source_file || get_source_file
|
@source_file || get_source_file
|
||||||
end
|
end
|
||||||
|
|
||||||
# Initialize resource with parent store and URL
|
# Initialize resource with parent store and URL
|
||||||
# @param [Middleman::Sitemap::Store] store
|
# @param [Middleman::Sitemap::Store] store
|
||||||
# @param [String] path
|
# @param [String] path
|
||||||
# @param [String] source_file
|
# @param [String] source_file
|
||||||
def initialize(store, path, source_file=nil)
|
def initialize(store, path, source_file=nil)
|
||||||
@store = store
|
@store = store
|
||||||
@app = @store.app
|
@app = @store.app
|
||||||
@path = path
|
@path = path
|
||||||
@source_file = source_file
|
@source_file = source_file
|
||||||
|
|
||||||
@destination_paths = [@path]
|
@destination_paths = [@path]
|
||||||
|
|
||||||
@local_metadata = { :options => {}, :locals => {}, :page => {}, :blocks => [] }
|
@local_metadata = { :options => {}, :locals => {}, :page => {}, :blocks => [] }
|
||||||
end
|
|
||||||
|
|
||||||
# Whether this resource has a template file
|
|
||||||
# @return [Boolean]
|
|
||||||
def template?
|
|
||||||
return false if source_file.nil?
|
|
||||||
!::Tilt[source_file].nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the metadata for both the current source_file and the current path
|
|
||||||
# @return [Hash]
|
|
||||||
def metadata
|
|
||||||
result = store.metadata_for_file(source_file).dup
|
|
||||||
|
|
||||||
path_meta = store.metadata_for_path(path).dup
|
|
||||||
if path_meta.has_key?(:blocks)
|
|
||||||
result[:blocks] << path_meta[:blocks]
|
|
||||||
path_meta.delete(:blocks)
|
|
||||||
end
|
end
|
||||||
result.deep_merge!(path_meta)
|
|
||||||
|
# Whether this resource has a template file
|
||||||
local_meta = @local_metadata.dup
|
# @return [Boolean]
|
||||||
if local_meta.has_key?(:blocks)
|
def template?
|
||||||
result[:blocks] << local_meta[:blocks]
|
return false if source_file.nil?
|
||||||
local_meta.delete(:blocks)
|
!::Tilt[source_file].nil?
|
||||||
end
|
end
|
||||||
result.deep_merge!(local_meta)
|
|
||||||
|
# Get the metadata for both the current source_file and the current path
|
||||||
|
# @return [Hash]
|
||||||
|
def metadata
|
||||||
|
result = store.metadata_for_file(source_file).dup
|
||||||
|
|
||||||
result
|
path_meta = store.metadata_for_path(path).dup
|
||||||
end
|
if path_meta.has_key?(:blocks)
|
||||||
|
result[:blocks] << path_meta[:blocks]
|
||||||
|
path_meta.delete(:blocks)
|
||||||
|
end
|
||||||
|
result.deep_merge!(path_meta)
|
||||||
|
|
||||||
# Merge in new metadata specific to this resource.
|
local_meta = @local_metadata.dup
|
||||||
# @param [Hash] metadata A metadata block like provides_metadata_for_path takes
|
if local_meta.has_key?(:blocks)
|
||||||
def add_metadata(metadata={}, &block)
|
result[:blocks] << local_meta[:blocks]
|
||||||
if metadata.has_key?(:blocks)
|
local_meta.delete(:blocks)
|
||||||
@local_metadata[:blocks] << metadata[:blocks]
|
end
|
||||||
metadata.delete(:blocks)
|
result.deep_merge!(local_meta)
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
# Merge in new metadata specific to this resource.
|
||||||
|
# @param [Hash] metadata A metadata block like provides_metadata_for_path takes
|
||||||
|
def add_metadata(metadata={}, &block)
|
||||||
|
if metadata.has_key?(:blocks)
|
||||||
|
@local_metadata[:blocks] << metadata[:blocks]
|
||||||
|
metadata.delete(:blocks)
|
||||||
|
end
|
||||||
|
@local_metadata.deep_merge(metadata)
|
||||||
|
@local_metadata[:blocks] << block if block_given?
|
||||||
end
|
end
|
||||||
@local_metadata.deep_merge(metadata)
|
|
||||||
@local_metadata[:blocks] << block if block_given?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the output/preview URL for this resource
|
# Get the output/preview URL for this resource
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def destination_path
|
def destination_path
|
||||||
@destination_paths.last
|
@destination_paths.last
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set the output/preview URL for this resource
|
# Set the output/preview URL for this resource
|
||||||
# @param [String] path
|
# @param [String] path
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def destination_path=(path)
|
def destination_path=(path)
|
||||||
@destination_paths << path
|
@destination_paths << path
|
||||||
end
|
end
|
||||||
|
|
||||||
# The template instance
|
# The template instance
|
||||||
# @return [Middleman::Sitemap::Template]
|
# @return [Middleman::Sitemap::Template]
|
||||||
def template
|
def template
|
||||||
@_template ||= ::Middleman::Sitemap::Template.new(self)
|
@_template ||= ::Middleman::Sitemap::Template.new(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Extension of the path (i.e. '.js')
|
# Extension of the path (i.e. '.js')
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def ext
|
def ext
|
||||||
File.extname(path)
|
File.extname(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Mime type of the path
|
# Mime type of the path
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def mime_type
|
def mime_type
|
||||||
app.mime_type ext
|
app.mime_type ext
|
||||||
end
|
end
|
||||||
|
|
||||||
# Render this resource
|
# Render this resource
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def render(opts={}, locs={}, &block)
|
def render(opts={}, locs={}, &block)
|
||||||
return File.open(source_file).read unless template?
|
return File.open(source_file).read unless template?
|
||||||
|
|
||||||
start_time = Time.now
|
start_time = Time.now
|
||||||
puts "== Render Start: #{source_file}" if app.logging?
|
puts "== Render Start: #{source_file}" if app.logging?
|
||||||
|
|
||||||
md = metadata.dup
|
md = metadata.dup
|
||||||
opts = md[:options].deep_merge(opts)
|
opts = md[:options].deep_merge(opts)
|
||||||
locs = md[:locals].deep_merge(locs)
|
locs = md[:locals].deep_merge(locs)
|
||||||
|
|
||||||
# Forward remaining data to helpers
|
# Forward remaining data to helpers
|
||||||
if md.has_key?(:page)
|
if md.has_key?(:page)
|
||||||
app.data.store("page", md[:page])
|
app.data.store("page", md[:page])
|
||||||
|
end
|
||||||
|
|
||||||
|
md[:blocks].flatten.compact.each do |block|
|
||||||
|
app.instance_eval(&block)
|
||||||
|
end
|
||||||
|
|
||||||
|
app.instance_eval(&block) if block_given?
|
||||||
|
result = app.render_template(source_file, locs, opts)
|
||||||
|
|
||||||
|
puts "== Render End: #{source_file} (#{(Time.now - start_time).round(2)}s)" if app.logging?
|
||||||
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
md[:blocks].flatten.compact.each do |block|
|
|
||||||
app.instance_eval(&block)
|
|
||||||
end
|
|
||||||
|
|
||||||
app.instance_eval(&block) if block_given?
|
|
||||||
result = app.render_template(source_file, locs, opts)
|
|
||||||
|
|
||||||
puts "== Render End: #{source_file} (#{(Time.now - start_time).round(2)}s)" if app.logging?
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
# A path without the directory index - so foo/index.html becomes
|
# A path without the directory index - so foo/index.html becomes
|
||||||
# just foo. Best for linking.
|
# just foo. Best for linking.
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def url
|
def url
|
||||||
'/' + destination_path.sub(/#{Regexp.escape(app.index_file)}$/, '')
|
'/' + destination_path.sub(/#{Regexp.escape(app.index_file)}$/, '')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,208 +1,211 @@
|
||||||
# Used for merging results of metadata callbacks
|
# Used for merging results of metadata callbacks
|
||||||
require "active_support/core_ext/hash/deep_merge"
|
require "active_support/core_ext/hash/deep_merge"
|
||||||
|
|
||||||
# Sitemap namespace
|
module Middleman
|
||||||
module Middleman::Sitemap
|
|
||||||
|
|
||||||
# The Store class
|
# Sitemap namespace
|
||||||
#
|
module Sitemap
|
||||||
# The Store manages a collection of Resource objects, which represent
|
|
||||||
# individual items in the sitemap. Resources are indexed by "source path",
|
# The Store class
|
||||||
# which is the path relative to the source directory, minus any template
|
#
|
||||||
# extensions. All "path" parameters used in this class are source paths.
|
# The Store manages a collection of Resource objects, which represent
|
||||||
class Store
|
# individual items in the sitemap. Resources are indexed by "source path",
|
||||||
|
# which is the path relative to the source directory, minus any template
|
||||||
|
# extensions. All "path" parameters used in this class are source paths.
|
||||||
|
class Store
|
||||||
|
|
||||||
# @return [Middleman::Application]
|
# @return [Middleman::Application]
|
||||||
attr_accessor :app
|
attr_accessor :app
|
||||||
|
|
||||||
# Initialize with parent app
|
# Initialize with parent app
|
||||||
# @param [Middleman::Application] app
|
# @param [Middleman::Application] app
|
||||||
def initialize(app)
|
def initialize(app)
|
||||||
@app = app
|
@app = app
|
||||||
@resources = []
|
@resources = []
|
||||||
@_cached_metadata = {}
|
|
||||||
@_lookup_cache = { :path => {}, :destination_path => {} }
|
|
||||||
@resource_list_manipulators = []
|
|
||||||
|
|
||||||
# Register classes which can manipulate the main site map list
|
|
||||||
register_resource_list_manipulator(:on_disk, Middleman::Sitemap::Extensions::OnDisk.new(self), false)
|
|
||||||
|
|
||||||
# Proxies
|
|
||||||
register_resource_list_manipulator(:proxies, @app.proxy_manager, false)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Register a klass which can manipulate the main site map list
|
|
||||||
# @param [Symbol] name Name of the manipulator for debugging
|
|
||||||
# @param [Class, Module] inst Abstract namespace which can update the resource list
|
|
||||||
# @param [Boolean] immediately_rebuild Whether the resource list should be immediately recalculated
|
|
||||||
# @return [void]
|
|
||||||
def register_resource_list_manipulator(name, inst, immediately_rebuild=true)
|
|
||||||
@resource_list_manipulators << [name, inst]
|
|
||||||
rebuild_resource_list!(:registered_new) if immediately_rebuild
|
|
||||||
end
|
|
||||||
|
|
||||||
# Rebuild the list of resources from scratch, using registed manipulators
|
|
||||||
# @return [void]
|
|
||||||
def rebuild_resource_list!(reason=nil)
|
|
||||||
@resources = @resource_list_manipulators.inject([]) do |result, (_, inst)|
|
|
||||||
inst.manipulate_resource_list(result)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Reset lookup cache
|
|
||||||
@_lookup_cache = { :path => {}, :destination_path => {} }
|
|
||||||
@resources.each do |resource|
|
|
||||||
@_lookup_cache[:path][resource.path] = resource
|
|
||||||
@_lookup_cache[:destination_path][resource.destination_path] = resource
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Find a resource given its original path
|
|
||||||
# @param [String] request_path The original path of a resource.
|
|
||||||
# @return [Middleman::Sitemap::Resource]
|
|
||||||
def find_resource_by_path(request_path)
|
|
||||||
request_path = ::Middleman::Util.normalize_path(request_path)
|
|
||||||
@_lookup_cache[:path][request_path]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Find a resource given its destination path
|
|
||||||
# @param [String] request_path The destination (output) path of a resource.
|
|
||||||
# @return [Middleman::Sitemap::Resource]
|
|
||||||
def find_resource_by_destination_path(request_path)
|
|
||||||
request_path = ::Middleman::Util.normalize_path(request_path)
|
|
||||||
@_lookup_cache[:destination_path][request_path]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the array of all resources
|
|
||||||
# @param [Boolean] include_ignored Whether to include ignored resources
|
|
||||||
# @return [Array<Middleman::Sitemap::Resource>]
|
|
||||||
def resources(include_ignored=false)
|
|
||||||
if include_ignored
|
|
||||||
@resources
|
|
||||||
else
|
|
||||||
@resources.reject(&:ignored?)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Register a handler to provide metadata on a file path
|
|
||||||
# @param [Regexp] matcher
|
|
||||||
# @return [Array<Array<Proc, Regexp>>]
|
|
||||||
def provides_metadata(matcher=nil, &block)
|
|
||||||
@_provides_metadata ||= []
|
|
||||||
@_provides_metadata << [block, matcher] if block_given?
|
|
||||||
@_provides_metadata
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the metadata for a specific file
|
|
||||||
# @param [String] source_file
|
|
||||||
# @return [Hash]
|
|
||||||
def metadata_for_file(source_file)
|
|
||||||
blank_metadata = { :options => {}, :locals => {}, :page => {}, :blocks => [] }
|
|
||||||
|
|
||||||
provides_metadata.inject(blank_metadata) do |result, (callback, matcher)|
|
|
||||||
next result if !matcher.nil? && !source_file.match(matcher)
|
|
||||||
|
|
||||||
metadata = callback.call(source_file)
|
|
||||||
|
|
||||||
if metadata.has_key?(:blocks)
|
|
||||||
result[:blocks] << metadata[:blocks]
|
|
||||||
metadata.delete(:blocks)
|
|
||||||
end
|
|
||||||
|
|
||||||
result.deep_merge(metadata)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Register a handler to provide metadata on a url path
|
|
||||||
# @param [Regexp] matcher
|
|
||||||
# @param [Symbol] origin an indicator of where this metadata came from - only one
|
|
||||||
# block per [matcher, origin] pair may exist.
|
|
||||||
# @return [Array<Array<Proc, Regexp>>]
|
|
||||||
def provides_metadata_for_path(matcher=nil, origin=nil, &block)
|
|
||||||
@_provides_metadata_for_path ||= []
|
|
||||||
if block_given?
|
|
||||||
if origin
|
|
||||||
existing_provider = @_provides_metadata_for_path.find {|b,m,o| o == origin && m == matcher}
|
|
||||||
end
|
|
||||||
|
|
||||||
if existing_provider
|
|
||||||
existing_provider[0] = block
|
|
||||||
else
|
|
||||||
@_provides_metadata_for_path << [block, matcher, origin]
|
|
||||||
end
|
|
||||||
|
|
||||||
@_cached_metadata = {}
|
@_cached_metadata = {}
|
||||||
end
|
@_lookup_cache = { :path => {}, :destination_path => {} }
|
||||||
@_provides_metadata_for_path
|
@resource_list_manipulators = []
|
||||||
end
|
|
||||||
|
|
||||||
# Get the metadata for a specific URL
|
|
||||||
# @param [String] request_path
|
|
||||||
# @return [Hash]
|
|
||||||
def metadata_for_path(request_path)
|
|
||||||
return @_cached_metadata[request_path] if @_cached_metadata[request_path]
|
|
||||||
|
|
||||||
blank_metadata = { :options => {}, :locals => {}, :page => {}, :blocks => [] }
|
|
||||||
|
|
||||||
@_cached_metadata[request_path] = provides_metadata_for_path.inject(blank_metadata) do |result, (callback, matcher)|
|
# Register classes which can manipulate the main site map list
|
||||||
case matcher
|
register_resource_list_manipulator(:on_disk, Middleman::Sitemap::Extensions::OnDisk.new(self), false)
|
||||||
when Regexp
|
|
||||||
next result unless request_path.match(matcher)
|
# Proxies
|
||||||
when String
|
register_resource_list_manipulator(:proxies, @app.proxy_manager, false)
|
||||||
next result unless File.fnmatch("/" + matcher.sub(%r{^/}, ''), "/#{request_path}")
|
|
||||||
end
|
|
||||||
|
|
||||||
metadata = callback.call(request_path)
|
|
||||||
|
|
||||||
if metadata.has_key?(:blocks)
|
|
||||||
result[:blocks] << metadata[:blocks]
|
|
||||||
metadata.delete(:blocks)
|
|
||||||
end
|
|
||||||
|
|
||||||
result.deep_merge(metadata)
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Get the URL path for an on-disk file
|
|
||||||
# @param [String] file
|
|
||||||
# @return [String]
|
|
||||||
def file_to_path(file)
|
|
||||||
file = File.expand_path(file, @app.root)
|
|
||||||
|
|
||||||
prefix = @app.source_dir.sub(/\/$/, "") + "/"
|
|
||||||
return false unless file.include?(prefix)
|
|
||||||
|
|
||||||
path = file.sub(prefix, "")
|
|
||||||
extensionless_path(path)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get a path without templating extensions
|
|
||||||
# @param [String] file
|
|
||||||
# @return [String]
|
|
||||||
def extensionless_path(file)
|
|
||||||
path = file.dup
|
|
||||||
|
|
||||||
end_of_the_line = false
|
# Register a klass which can manipulate the main site map list
|
||||||
while !end_of_the_line
|
# @param [Symbol] name Name of the manipulator for debugging
|
||||||
if !::Tilt[path].nil?
|
# @param [Class, Module] inst Abstract namespace which can update the resource list
|
||||||
path = path.sub(File.extname(path), "")
|
# @param [Boolean] immediately_rebuild Whether the resource list should be immediately recalculated
|
||||||
|
# @return [void]
|
||||||
|
def register_resource_list_manipulator(name, inst, immediately_rebuild=true)
|
||||||
|
@resource_list_manipulators << [name, inst]
|
||||||
|
rebuild_resource_list!(:registered_new) if immediately_rebuild
|
||||||
|
end
|
||||||
|
|
||||||
|
# Rebuild the list of resources from scratch, using registed manipulators
|
||||||
|
# @return [void]
|
||||||
|
def rebuild_resource_list!(reason=nil)
|
||||||
|
@resources = @resource_list_manipulators.inject([]) do |result, (_, inst)|
|
||||||
|
inst.manipulate_resource_list(result)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reset lookup cache
|
||||||
|
@_lookup_cache = { :path => {}, :destination_path => {} }
|
||||||
|
@resources.each do |resource|
|
||||||
|
@_lookup_cache[:path][resource.path] = resource
|
||||||
|
@_lookup_cache[:destination_path][resource.destination_path] = resource
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find a resource given its original path
|
||||||
|
# @param [String] request_path The original path of a resource.
|
||||||
|
# @return [Middleman::Sitemap::Resource]
|
||||||
|
def find_resource_by_path(request_path)
|
||||||
|
request_path = ::Middleman::Util.normalize_path(request_path)
|
||||||
|
@_lookup_cache[:path][request_path]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Find a resource given its destination path
|
||||||
|
# @param [String] request_path The destination (output) path of a resource.
|
||||||
|
# @return [Middleman::Sitemap::Resource]
|
||||||
|
def find_resource_by_destination_path(request_path)
|
||||||
|
request_path = ::Middleman::Util.normalize_path(request_path)
|
||||||
|
@_lookup_cache[:destination_path][request_path]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the array of all resources
|
||||||
|
# @param [Boolean] include_ignored Whether to include ignored resources
|
||||||
|
# @return [Array<Middleman::Sitemap::Resource>]
|
||||||
|
def resources(include_ignored=false)
|
||||||
|
if include_ignored
|
||||||
|
@resources
|
||||||
else
|
else
|
||||||
end_of_the_line = true
|
@resources.reject(&:ignored?)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Register a handler to provide metadata on a file path
|
||||||
|
# @param [Regexp] matcher
|
||||||
|
# @return [Array<Array<Proc, Regexp>>]
|
||||||
|
def provides_metadata(matcher=nil, &block)
|
||||||
|
@_provides_metadata ||= []
|
||||||
|
@_provides_metadata << [block, matcher] if block_given?
|
||||||
|
@_provides_metadata
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the metadata for a specific file
|
||||||
|
# @param [String] source_file
|
||||||
|
# @return [Hash]
|
||||||
|
def metadata_for_file(source_file)
|
||||||
|
blank_metadata = { :options => {}, :locals => {}, :page => {}, :blocks => [] }
|
||||||
|
|
||||||
|
provides_metadata.inject(blank_metadata) do |result, (callback, matcher)|
|
||||||
|
next result if !matcher.nil? && !source_file.match(matcher)
|
||||||
|
|
||||||
|
metadata = callback.call(source_file)
|
||||||
|
|
||||||
# If there is no extension, look for one
|
if metadata.has_key?(:blocks)
|
||||||
if File.extname(path).empty?
|
result[:blocks] << metadata[:blocks]
|
||||||
input_ext = File.extname(file)
|
metadata.delete(:blocks)
|
||||||
|
end
|
||||||
|
|
||||||
if !input_ext.empty?
|
result.deep_merge(metadata)
|
||||||
input_ext = input_ext.split(".").last.to_sym
|
end
|
||||||
if @app.template_extensions.has_key?(input_ext)
|
end
|
||||||
path << ".#{@app.template_extensions[input_ext]}"
|
|
||||||
|
# Register a handler to provide metadata on a url path
|
||||||
|
# @param [Regexp] matcher
|
||||||
|
# @param [Symbol] origin an indicator of where this metadata came from - only one
|
||||||
|
# block per [matcher, origin] pair may exist.
|
||||||
|
# @return [Array<Array<Proc, Regexp>>]
|
||||||
|
def provides_metadata_for_path(matcher=nil, origin=nil, &block)
|
||||||
|
@_provides_metadata_for_path ||= []
|
||||||
|
if block_given?
|
||||||
|
if origin
|
||||||
|
existing_provider = @_provides_metadata_for_path.find {|b,m,o| o == origin && m == matcher}
|
||||||
|
end
|
||||||
|
|
||||||
|
if existing_provider
|
||||||
|
existing_provider[0] = block
|
||||||
|
else
|
||||||
|
@_provides_metadata_for_path << [block, matcher, origin]
|
||||||
|
end
|
||||||
|
|
||||||
|
@_cached_metadata = {}
|
||||||
|
end
|
||||||
|
@_provides_metadata_for_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the metadata for a specific URL
|
||||||
|
# @param [String] request_path
|
||||||
|
# @return [Hash]
|
||||||
|
def metadata_for_path(request_path)
|
||||||
|
return @_cached_metadata[request_path] if @_cached_metadata[request_path]
|
||||||
|
|
||||||
|
blank_metadata = { :options => {}, :locals => {}, :page => {}, :blocks => [] }
|
||||||
|
|
||||||
|
@_cached_metadata[request_path] = provides_metadata_for_path.inject(blank_metadata) do |result, (callback, matcher)|
|
||||||
|
case matcher
|
||||||
|
when Regexp
|
||||||
|
next result unless request_path.match(matcher)
|
||||||
|
when String
|
||||||
|
next result unless File.fnmatch("/" + matcher.sub(%r{^/}, ''), "/#{request_path}")
|
||||||
|
end
|
||||||
|
|
||||||
|
metadata = callback.call(request_path)
|
||||||
|
|
||||||
|
if metadata.has_key?(:blocks)
|
||||||
|
result[:blocks] << metadata[:blocks]
|
||||||
|
metadata.delete(:blocks)
|
||||||
|
end
|
||||||
|
|
||||||
|
result.deep_merge(metadata)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the URL path for an on-disk file
|
||||||
|
# @param [String] file
|
||||||
|
# @return [String]
|
||||||
|
def file_to_path(file)
|
||||||
|
file = File.expand_path(file, @app.root)
|
||||||
|
|
||||||
|
prefix = @app.source_dir.sub(/\/$/, "") + "/"
|
||||||
|
return false unless file.include?(prefix)
|
||||||
|
|
||||||
|
path = file.sub(prefix, "")
|
||||||
|
extensionless_path(path)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get a path without templating extensions
|
||||||
|
# @param [String] file
|
||||||
|
# @return [String]
|
||||||
|
def extensionless_path(file)
|
||||||
|
path = file.dup
|
||||||
|
|
||||||
|
end_of_the_line = false
|
||||||
|
while !end_of_the_line
|
||||||
|
if !::Tilt[path].nil?
|
||||||
|
path = path.sub(File.extname(path), "")
|
||||||
|
else
|
||||||
|
end_of_the_line = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
path
|
# If there is no extension, look for one
|
||||||
|
if File.extname(path).empty?
|
||||||
|
input_ext = File.extname(file)
|
||||||
|
|
||||||
|
if !input_ext.empty?
|
||||||
|
input_ext = input_ext.split(".").last.to_sym
|
||||||
|
if @app.template_extensions.has_key?(input_ext)
|
||||||
|
path << ".#{@app.template_extensions[input_ext]}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
path
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
Loading…
Reference in a new issue