diff --git a/middleman-core/bin/middleman b/middleman-core/bin/middleman index 5ee38897..cd2e7c1c 100755 --- a/middleman-core/bin/middleman +++ b/middleman-core/bin/middleman @@ -12,7 +12,7 @@ require "pathname" # Recursive method to find config.rb def locate_root(cwd = Pathname.new(Dir.pwd)) - return cwd.to_s if File.exists?(File.join(cwd, 'config.rb')) + return cwd.to_s if (cwd + 'config.rb').exists? return false if cwd.root? locate_root(cwd.parent) end diff --git a/middleman-core/lib/middleman-core/application.rb b/middleman-core/lib/middleman-core/application.rb index 32cddf28..9a9ec5aa 100644 --- a/middleman-core/lib/middleman-core/application.rb +++ b/middleman-core/lib/middleman-core/application.rb @@ -72,6 +72,11 @@ module Middleman # @return [String] set :root, ENV["MM_ROOT"] || Dir.pwd + # Pathname-addressed root + def root_path + @_root_path ||= Pathname.new(root) + end + # Name of the source directory # @return [String] set :source, "source" diff --git a/middleman-core/lib/middleman-core/cli/build.rb b/middleman-core/lib/middleman-core/cli/build.rb index 7754b91c..3f2574f6 100644 --- a/middleman-core/lib/middleman-core/cli/build.rb +++ b/middleman-core/lib/middleman-core/cli/build.rb @@ -210,7 +210,7 @@ module Middleman::Cli puts "== Checking for Compass sprites" if @app.logging? # Double-check for compass sprites - @app.files.find_new_files(File.join(@app.source_dir, @app.images_dir)) + @app.files.find_new_files(Pathname.new(@app.source_dir) + @app.images_dir) # Sort paths to be built by the above order. This is primarily so Compass can # find files in the build folder when it needs to generate sprites for the diff --git a/middleman-core/lib/middleman-core/core_extensions/file_watcher.rb b/middleman-core/lib/middleman-core/core_extensions/file_watcher.rb index 0e33af37..c7cc26e5 100644 --- a/middleman-core/lib/middleman-core/core_extensions/file_watcher.rb +++ b/middleman-core/lib/middleman-core/core_extensions/file_watcher.rb @@ -1,4 +1,4 @@ -require "find" +require "pathname" require "set" # API for watching file change events @@ -7,6 +7,7 @@ module Middleman module FileWatcher IGNORE_LIST = [ + /^\.bundle\//, /^\.sass-cache\//, /^\.git\//, /^\.gitignore$/, @@ -23,33 +24,21 @@ module Middleman # Once registered def registered(app) - app.extend ClassMethods app.send :include, InstanceMethods # Before parsing config, load the data/ directory app.before_configuration do - data_path = File.join(root, data_dir) - files.reload_path(data_path) if File.exists?(data_path) + files.reload_path(root_path + data_dir) end # After config, load everything else app.ready do - files.reload_path(root) + files.reload_path(root_path) end end alias :included :registered end - # Class methods - module ClassMethods - - # Access the file api - # @return [Middleman::CoreExtensions::FileWatcher::API] - def files - @_files_api ||= API.new - end - end - # Instance methods module InstanceMethods @@ -92,68 +81,77 @@ module Middleman # Notify callbacks that a file changed # - # @param [String] path The file that changed + # @param [Pathname] path The file that changed # @return [void] def did_change(path) - return if IGNORE_LIST.any? { |r| path.match(r) } - puts "== File Change: #{path}" if @app.logging? + return if ignored?(path) + puts "== File Change: #{path.relative_path_from(@app.root_path)}" if @app.logging? @known_paths << path self.run_callbacks(path, :changed) end # Notify callbacks that a file was deleted # - # @param [String] path The file that was deleted + # @param [Pathname] path The file that was deleted # @return [void] def did_delete(path) - return if IGNORE_LIST.any? { |r| path.match(r) } - puts "== File Deletion: #{path}" if @app.logging? + return if ignored?(path) + puts "== File Deletion: #{path.relative_path_from(@app.root_path)}" if @app.logging? @known_paths.delete(path) self.run_callbacks(path, :deleted) end # Manually trigger update events # - # @param [String] path The path to reload + # @param [Pathname] path The path to reload + # @param [Boolean] only_new Whether we only look for new files # @return [void] - def reload_path(path) - relative_path = path.sub("#{@app.root}/", "") - subset = @known_paths.select { |p| p.match(%r{^#{relative_path}}) } - - Find.find(path) do |path| - next if File.directory?(path) - relative_path = path.sub("#{@app.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) + def reload_path(path, only_new=false) + return unless path.exists? + + glob = "#{path}**/*" + subset = @known_paths.select { |p| p.fnmatch(glob) } + + path.find do |filepath| + full_path = path + filepath + next if full_path.directory? + + if only_new + next if subset.include?(full_path) + else + subset.delete(full_path) + end + + self.did_change(full_path) end + + subset.each(&method(:did_delete)) unless only_new end # Like reload_path, but only triggers events on new files # - # @param [String] path The path to reload + # @param [Pathname] path The path to reload # @return [void] def find_new_files(path) - relative_path = path.sub("#{@app.root}/", "") - subset = @known_paths.select { |p| p.match(%r{^#{relative_path}}) } - - Find.find(path) do |file| - next if File.directory?(file) - relative_path = file.sub("#{@app.root}/", "") - self.did_change(relative_path) unless subset.include?(relative_path) - end if File.exists?(path) + reload_path(path, true) end protected + # Whether this path is ignored + # @param [Pathname] path + # @return [Boolean] + def ignored?(path) + path = path.relative_path_from(@app.root_path).to_s if path.is_a? Pathname + IGNORE_LIST.any? { |r| path.to_s.match(r) } + end + # Notify callbacks for a file given an array of callbacks # - # @param [String] path The file that was changed + # @param [Pathname] path The file that was changed # @param [Symbol] callbacks_name The name of the callbacks method # @return [void] def run_callbacks(path, callbacks_name) + path = path.relative_path_from(@app.root_path).to_s if path.is_a? Pathname self.send(callbacks_name).each do |callback, matcher| next if path.match(%r{^#{@app.build_dir}/}) next unless matcher.nil? || path.match(matcher) @@ -163,4 +161,4 @@ module Middleman end end end -end +end \ No newline at end of file diff --git a/middleman-core/lib/middleman-core/preview_server.rb b/middleman-core/lib/middleman-core/preview_server.rb index afe53b14..ed4bdb47 100644 --- a/middleman-core/lib/middleman-core/preview_server.rb +++ b/middleman-core/lib/middleman-core/preview_server.rb @@ -83,7 +83,7 @@ module Middleman # Otherwise forward to Middleman added_and_modified.each do |path| - @app.files.did_change(path) + @app.files.did_change(@app.root_path + path) end end @@ -93,7 +93,7 @@ module Middleman # Otherwise forward to Middleman removed.each do |path| - @app.files.did_delete(path) + @app.files.did_delete(@app.root_path + path) end end end diff --git a/middleman-core/lib/middleman-core/step_definitions/middleman_steps.rb b/middleman-core/lib/middleman-core/step_definitions/middleman_steps.rb index 1ba211fe..288f089f 100644 --- a/middleman-core/lib/middleman-core/step_definitions/middleman_steps.rb +++ b/middleman-core/lib/middleman-core/step_definitions/middleman_steps.rb @@ -9,9 +9,9 @@ Then /^the file "([^\"]*)" is removed$/ do |path| end Then /^the file "([^\"]*)" did change$/ do |path| - @server_inst.files.did_change(path) + @server_inst.files.did_change(@server_inst.root_path + path) end Then /^the file "([^\"]*)" did delete$/ do |path| - @server_inst.files.did_delete(path) + @server_inst.files.did_delete(@server_inst.root_path + path) end \ No newline at end of file diff --git a/middleman-core/lib/middleman-core/util.rb b/middleman-core/lib/middleman-core/util.rb index 732e689f..7d69dedc 100644 --- a/middleman-core/lib/middleman-core/util.rb +++ b/middleman-core/lib/middleman-core/util.rb @@ -1,8 +1,12 @@ # Using Thor's indifferent hash access require "thor" +# Core Pathname library used for traversal +require "pathname" + module Middleman module Util + # Recursively convert a normal Hash into a HashWithIndifferentAccess # # @private