refactor file change api, add a disable-watcher command line option for heroku/hosted
This commit is contained in:
parent
3d3e9e2f5b
commit
20fea1bab1
9 changed files with 126 additions and 68 deletions
|
@ -322,9 +322,9 @@ class Middleman::Base
|
||||||
|
|
||||||
if env["PATH_INFO"] == "/__middleman__" && env["REQUEST_METHOD"] == "POST"
|
if env["PATH_INFO"] == "/__middleman__" && env["REQUEST_METHOD"] == "POST"
|
||||||
if req.params.has_key?("change")
|
if req.params.has_key?("change")
|
||||||
file_did_change(req.params["change"])
|
self.files.did_change(req.params["change"])
|
||||||
elsif req.params.has_key?("delete")
|
elsif req.params.has_key?("delete")
|
||||||
file_did_delete(req.params["delete"])
|
self.files.did_delete(req.params["delete"])
|
||||||
end
|
end
|
||||||
|
|
||||||
res.status = 200
|
res.status = 200
|
||||||
|
|
|
@ -8,7 +8,7 @@ module Middleman::Cli
|
||||||
namespace :server
|
namespace :server
|
||||||
|
|
||||||
desc "server [options]", "Start the preview server"
|
desc "server [options]", "Start the preview server"
|
||||||
method_option "environment",
|
method_option :environment,
|
||||||
:aliases => "-e",
|
:aliases => "-e",
|
||||||
:default => ENV['MM_ENV'] || ENV['RACK_ENV'] || 'development',
|
:default => ENV['MM_ENV'] || ENV['RACK_ENV'] || 'development',
|
||||||
:desc => "The environment Middleman will run under"
|
:desc => "The environment Middleman will run under"
|
||||||
|
@ -17,29 +17,34 @@ module Middleman::Cli
|
||||||
:aliases => "-h",
|
:aliases => "-h",
|
||||||
:default => "0.0.0.0",
|
:default => "0.0.0.0",
|
||||||
:desc => "Bind to HOST address"
|
:desc => "Bind to HOST address"
|
||||||
method_option "port",
|
method_option :port,
|
||||||
:aliases => "-p",
|
:aliases => "-p",
|
||||||
:default => "4567",
|
:default => "4567",
|
||||||
:desc => "The port Middleman will listen on"
|
:desc => "The port Middleman will listen on"
|
||||||
method_option "verbose",
|
method_option :verbose,
|
||||||
:type => :boolean,
|
:type => :boolean,
|
||||||
:default => false,
|
:default => false,
|
||||||
:desc => 'Print debug messages'
|
:desc => 'Print debug messages'
|
||||||
|
method_option "disable-watcher",
|
||||||
|
:type => :boolean,
|
||||||
|
:default => false,
|
||||||
|
:desc => 'Disable the file change and delete watcher process'
|
||||||
|
|
||||||
# Start the server
|
# Start the server
|
||||||
def server
|
def server
|
||||||
if !ENV["MM_ROOT"]
|
if !ENV["MM_ROOT"]
|
||||||
puts "== Warning: Could not find a Middleman project config.rb"
|
puts "== Could not find a Middleman project config.rb"
|
||||||
puts "== Treating directory as a static site to be served"
|
puts "== Treating directory as a static site to be served"
|
||||||
ENV["MM_ROOT"] = Dir.pwd
|
ENV["MM_ROOT"] = Dir.pwd
|
||||||
ENV["MM_SOURCE"] = ""
|
ENV["MM_SOURCE"] = ""
|
||||||
end
|
end
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
:port => options["port"],
|
:port => options["port"],
|
||||||
:host => options["host"],
|
:host => options["host"],
|
||||||
:environment => options["environment"],
|
:environment => options["environment"],
|
||||||
:debug => options["verbose"]
|
:debug => options["verbose"],
|
||||||
|
:"disable-watcher" => options["disable-watcher"]
|
||||||
}
|
}
|
||||||
|
|
||||||
puts "== The Middleman is loading"
|
puts "== The Middleman is loading"
|
||||||
|
|
|
@ -21,12 +21,12 @@ module Middleman::CoreExtensions::Data
|
||||||
# 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
|
||||||
file_changed DataStore.matcher do |file|
|
self.files.changed DataStore.matcher do |file|
|
||||||
data.touch_file(file) if file.match(%r{^#{data_dir}\/})
|
self.data.touch_file(file) if file.match(%r{^#{self.data_dir}\/})
|
||||||
end
|
end
|
||||||
|
|
||||||
file_deleted DataStore.matcher do |file|
|
self.files.deleted DataStore.matcher do |file|
|
||||||
data.remove_file(file) if file.match(%r{^#{data_dir}\/})
|
self.data.remove_file(file) if file.match(%r{^#{self.data_dir}\/})
|
||||||
end
|
end
|
||||||
|
|
||||||
super
|
super
|
||||||
|
|
|
@ -2,30 +2,24 @@ require "find"
|
||||||
|
|
||||||
# API for watching file change events
|
# API for watching file change events
|
||||||
module Middleman::CoreExtensions::FileWatcher
|
module Middleman::CoreExtensions::FileWatcher
|
||||||
|
|
||||||
# Setup extension
|
# Setup extension
|
||||||
class << self
|
class << self
|
||||||
# @private
|
|
||||||
|
# Once registered
|
||||||
def registered(app)
|
def registered(app)
|
||||||
app.extend ClassMethods
|
app.extend ClassMethods
|
||||||
app.send :include, InstanceMethods
|
app.send :include, InstanceMethods
|
||||||
|
|
||||||
app.delegate :file_changed, :file_deleted, :to => :"self.class"
|
|
||||||
|
|
||||||
# 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(root, data_dir)
|
data_path = File.join(self.root, self.data_dir)
|
||||||
Find.find(data_path) do |path|
|
self.files.reload_path(data_path) if File.exists?(data_path)
|
||||||
next if File.directory?(path)
|
|
||||||
file_did_change(path.sub("#{root}/", ""))
|
|
||||||
end if File.exists?(data_path)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# After config, load everything else
|
# After config, load everything else
|
||||||
app.ready do
|
app.ready do
|
||||||
Find.find(root) do |path|
|
self.files.reload_path(self.root)
|
||||||
next if File.directory?(path)
|
|
||||||
file_did_change(path.sub("#{root}/", ""))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
alias :included :registered
|
alias :included :registered
|
||||||
|
@ -33,52 +27,105 @@ module Middleman::CoreExtensions::FileWatcher
|
||||||
|
|
||||||
# Class methods
|
# Class methods
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
|
|
||||||
|
# Access the file api
|
||||||
|
# @return [Middleman::CoreExtensions::FileWatcher::API]
|
||||||
|
def files
|
||||||
|
@_files_api ||= API.new
|
||||||
|
end
|
||||||
|
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 = []
|
||||||
|
end
|
||||||
|
|
||||||
# Add callback to be run on file change
|
# Add callback to be run on file change
|
||||||
#
|
#
|
||||||
# @param [nil,Regexp] matcher A Regexp to match the change path against
|
# @param [nil,Regexp] matcher A Regexp to match the change path against
|
||||||
# @return [Array<Proc>]
|
# @return [Array<Proc>]
|
||||||
def file_changed(matcher=nil, &block)
|
def changed(matcher=nil, &block)
|
||||||
@_file_changed ||= []
|
@_changed ||= []
|
||||||
@_file_changed << [block, matcher] if block_given?
|
@_changed << [block, matcher] if block_given?
|
||||||
@_file_changed
|
@_changed
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add callback to be run on file deletion
|
# Add callback to be run on file deletion
|
||||||
#
|
#
|
||||||
# @param [nil,Regexp] matcher A Regexp to match the deleted path against
|
# @param [nil,Regexp] matcher A Regexp to match the deleted path against
|
||||||
# @return [Array<Proc>]
|
# @return [Array<Proc>]
|
||||||
def file_deleted(matcher=nil, &block)
|
def deleted(matcher=nil, &block)
|
||||||
@_file_deleted ||= []
|
@_deleted ||= []
|
||||||
@_file_deleted << [block, matcher] if block_given?
|
@_deleted << [block, matcher] if block_given?
|
||||||
@_file_deleted
|
@_deleted
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Instance methods
|
|
||||||
module InstanceMethods
|
|
||||||
# Notify callbacks that a file changed
|
# Notify callbacks that a file changed
|
||||||
#
|
#
|
||||||
# @param [String] path The file that changed
|
# @param [String] path The file that changed
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def file_did_change(path)
|
def did_change(path)
|
||||||
return if ::Middleman::Watcher.ignore_list.any? { |r| path.match(r) }
|
self.known_paths << path unless self.known_paths.include?(path)
|
||||||
file_changed.each do |callback, matcher|
|
self.run_callbacks(path, :changed)
|
||||||
next if path.match(%r{^#{build_dir}/})
|
|
||||||
next if !matcher.nil? && !path.match(matcher)
|
|
||||||
instance_exec(path, &callback)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Notify callbacks that a file was deleted
|
# Notify callbacks that a file was deleted
|
||||||
#
|
#
|
||||||
# @param [String] path The file that was deleted
|
# @param [String] path The file that was deleted
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def file_did_delete(path)
|
def did_delete(path)
|
||||||
|
self.known_paths.delete(path) if self.known_paths.include?(path)
|
||||||
|
self.run_callbacks(path, :deleted)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Manually trigger update events
|
||||||
|
#
|
||||||
|
# @param [String] path The path to reload
|
||||||
|
# @return [void]
|
||||||
|
def reload_path(path)
|
||||||
|
subset = self.known_paths.select { |p| p.match(%r{^#{path}}) }
|
||||||
|
|
||||||
|
Find.find(path) do |path|
|
||||||
|
next if File.directory?(path)
|
||||||
|
relative_path = path.sub("#{self.instance.root}/", "")
|
||||||
|
subset.delete(relative_path) if subset.include?(relative_path)
|
||||||
|
self.did_change(relative_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
subset.each do |removed_path|
|
||||||
|
self.did_delete(removed_path)
|
||||||
|
end
|
||||||
|
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) }
|
return if ::Middleman::Watcher.ignore_list.any? { |r| path.match(r) }
|
||||||
file_deleted.each do |callback, matcher|
|
|
||||||
next if path.match(%r{^#{build_dir}/})
|
self.send(callbacks_name).each do |callback, matcher|
|
||||||
|
next if path.match(%r{^#{self.instance.build_dir}/})
|
||||||
next unless matcher.nil? || path.match(matcher)
|
next unless matcher.nil? || path.match(matcher)
|
||||||
instance_exec(path, &callback)
|
self.instance.instance_exec(path, &callback)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,12 +30,12 @@ module Middleman::CoreExtensions::FrontMatter
|
||||||
|
|
||||||
matcher = %r{#{static_path}.*(#{exts})}
|
matcher = %r{#{static_path}.*(#{exts})}
|
||||||
|
|
||||||
file_changed matcher do |file|
|
self.files.changed matcher do |file|
|
||||||
frontmatter_extension.touch_file(file)
|
self.frontmatter_extension.touch_file(file)
|
||||||
end
|
end
|
||||||
|
|
||||||
file_deleted matcher do |file|
|
self.files.deleted matcher do |file|
|
||||||
frontmatter_extension.remove_file(file)
|
self.frontmatter_extension.remove_file(file)
|
||||||
end
|
end
|
||||||
|
|
||||||
provides_metadata matcher do |path|
|
provides_metadata matcher do |path|
|
||||||
|
|
|
@ -26,9 +26,9 @@ module Middleman::CoreExtensions::Rendering
|
||||||
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 + "/"}})
|
||||||
|
|
||||||
file_changed render_regex do |file|
|
self.files.changed render_regex do |file|
|
||||||
path = File.expand_path(file, root)
|
path = File.expand_path(file, self.root)
|
||||||
cache.remove(:raw_template, path)
|
self.cache.remove(:raw_template, path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -31,12 +31,12 @@ module Middleman::CoreExtensions::Sitemap
|
||||||
static_path = source_dir.sub(self.root, "").sub(/^\//, "")
|
static_path = source_dir.sub(self.root, "").sub(/^\//, "")
|
||||||
sitemap_regex = static_path.empty? ? // : (%r{^#{static_path + "/"}})
|
sitemap_regex = static_path.empty? ? // : (%r{^#{static_path + "/"}})
|
||||||
|
|
||||||
file_changed sitemap_regex do |file|
|
self.files.changed sitemap_regex do |file|
|
||||||
sitemap.touch_file(file)
|
self.sitemap.touch_file(file)
|
||||||
end
|
end
|
||||||
|
|
||||||
file_deleted sitemap_regex do |file|
|
self.files.deleted sitemap_regex do |file|
|
||||||
sitemap.remove_file(file)
|
self.sitemap.remove_file(file)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@ Then /^the file "([^\"]*)" is removed$/ do |path|
|
||||||
end
|
end
|
||||||
|
|
||||||
Then /^the file "([^\"]*)" did change$/ do |path|
|
Then /^the file "([^\"]*)" did change$/ do |path|
|
||||||
@server_inst.file_did_change(path)
|
@server_inst.files.did_change(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
Then /^the file "([^\"]*)" did delete$/ do |path|
|
Then /^the file "([^\"]*)" did delete$/ do |path|
|
||||||
@server_inst.file_did_delete(path)
|
@server_inst.files.did_delete(path)
|
||||||
end
|
end
|
|
@ -14,7 +14,7 @@ module Middleman
|
||||||
|
|
||||||
def start(options)
|
def start(options)
|
||||||
self.singleton = new(options)
|
self.singleton = new(options)
|
||||||
self.singleton.watch!
|
self.singleton.watch! unless options[:"disable-watcher"]
|
||||||
end
|
end
|
||||||
|
|
||||||
# What command is sent to kill instances
|
# What command is sent to kill instances
|
||||||
|
@ -58,10 +58,14 @@ module Middleman
|
||||||
# Start Middleman in a fork
|
# Start Middleman in a fork
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def start
|
def start
|
||||||
@server_job = fork {
|
if @options[:"disable-watcher"]
|
||||||
Signal.trap(::Middleman::WINDOWS ? :KILL : :TERM) { exit! }
|
|
||||||
bootup
|
bootup
|
||||||
}
|
else
|
||||||
|
@server_job = fork {
|
||||||
|
Signal.trap(::Middleman::WINDOWS ? :KILL : :TERM) { exit! }
|
||||||
|
bootup
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Start an instance of Middleman::Base
|
# Start an instance of Middleman::Base
|
||||||
|
@ -87,9 +91,11 @@ module Middleman
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def stop
|
def stop
|
||||||
puts "== The Middleman is shutting down"
|
puts "== The Middleman is shutting down"
|
||||||
Process.kill(::Middleman::WINDOWS ? :KILL : :TERM, @server_job)
|
if !@options[:"disable-watcher"]
|
||||||
Process.wait @server_job
|
Process.kill(::Middleman::WINDOWS ? :KILL : :TERM, @server_job)
|
||||||
@server_job = nil
|
Process.wait @server_job
|
||||||
|
@server_job = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Simply stop, then start
|
# Simply stop, then start
|
||||||
|
|
Loading…
Add table
Reference in a new issue