Generic related files check

This commit is contained in:
Thomas Reynolds 2015-11-28 16:48:08 -08:00
parent a546e0f523
commit 16b997498b
15 changed files with 149 additions and 49 deletions

View file

@ -61,3 +61,5 @@ Style/ParallelAssignment:
Enabled: false Enabled: false
Style/BlockDelimiters: Style/BlockDelimiters:
Enabled: false Enabled: false
Style/MultilineBlockChain:
Enabled: false

View file

@ -7,6 +7,7 @@ gem 'yard', '~> 0.8', require: false
# Test tools # Test tools
gem 'pry', '~> 0.10', group: :development, require: false gem 'pry', '~> 0.10', group: :development, require: false
gem 'pry-byebug' gem 'pry-byebug'
gem 'pry-stack_explorer'
gem 'aruba', '~> 0.7.4', require: false gem 'aruba', '~> 0.7.4', require: false
gem 'rspec', '~> 3.0', require: false gem 'rspec', '~> 3.0', require: false
gem 'cucumber', '~> 2.0', require: false gem 'cucumber', '~> 2.0', require: false

View file

@ -180,7 +180,7 @@ module Middleman
Contract String => String Contract String => String
def extensionless_path(file) def extensionless_path(file)
path = file.dup path = file.dup
remove_templating_extensions(path) ::Middleman::Util.remove_templating_extensions(path)
end end
# Actually update the resource list, assuming anything has called # Actually update the resource list, assuming anything has called
@ -224,16 +224,6 @@ module Middleman
end end
end end
# Removes the templating extensions, while keeping the others
# @param [String] path
# @return [String]
Contract String => String
def remove_templating_extensions(path)
# Strip templating extensions as long as Tilt knows them
path = path.sub(/#{::Regexp.escape(File.extname(path))}$/, '') while ::Tilt[path]
path
end
# Remove the locale token from the end of the path # Remove the locale token from the end of the path
# @param [String] path # @param [String] path
# @return [String] # @return [String]

View file

@ -225,16 +225,24 @@ module Middleman
# @return [void] # @return [void]
Contract ArrayOf[Pathname], ArrayOf[Pathname] => Any Contract ArrayOf[Pathname], ArrayOf[Pathname] => Any
def update(updated_paths, removed_paths) def update(updated_paths, removed_paths)
valid_updates = (updated_paths | find_related_files(updated_paths + removed_paths)) valid_updates = updated_paths
.lazy .map { |p| ::Middleman::Util.path_to_source_file(p, @directory, @type) }
.map(&method(:path_to_source_file))
.select(&method(:valid?)) .select(&method(:valid?))
.to_a
.each do |f| valid_updates.each do |f|
add_file_to_cache(f) add_file_to_cache(f)
logger.debug "== Change (#{f[:types].inspect}): #{f[:relative_path]}" logger.debug "== Change (#{f[:types].inspect}): #{f[:relative_path]}"
end end
related_updates = ::Middleman::Util.find_related_files(app, (updated_paths + removed_paths)).select(&method(:valid?))
related_updates.each do |f|
add_file_to_cache(f)
logger.debug "== Possible Change (#{f[:types].inspect}): #{f[:relative_path]}"
end
valid_updates |= related_updates
valid_removes = removed_paths valid_removes = removed_paths
.lazy .lazy
.select(&@files.method(:key?)) .select(&@files.method(:key?))
@ -283,35 +291,5 @@ module Middleman
@only.any? { |reg| reg.match(file[:relative_path].to_s) } @only.any? { |reg| reg.match(file[:relative_path].to_s) }
end end
end end
# Convert a path to a file resprentation.
#
# @param [Pathname] path The path.
# @return [Middleman::SourceFile]
Contract Pathname => IsA['Middleman::SourceFile']
def path_to_source_file(path)
types = Set.new([@type])
relative_path = path.relative_path_from(@directory)
destination_dir = @options.fetch(:destination_dir, false)
relative_path = File.join(destination_dir, relative_path) if destination_dir
::Middleman::SourceFile.new(Pathname(relative_path), path, @directory, types)
end
# Finds files which should also be considered to be dirty when
# the given file(s) are touched.
#
# @param [Pathname] files The original touched file paths.
# @return [Pathname] All related file paths, not including the source file paths.
Contract ArrayOf[Pathname] => ArrayOf[Pathname]
def find_related_files(files)
files.flat_map do |file|
# If any partial file changes, reload all non-partials
if File.basename(file).start_with?('_')
Dir[File.join(@directory, app.config[:source], "**/[^_]*.#{File.extname(file)}")].map { |p| Pathname(p) }
end
end.compact.uniq - files
end
end end
end end

View file

@ -430,6 +430,95 @@ module Middleman
end end
end end
Contract String => String
def step_through_extensions(path)
while ::Tilt[path]
yield File.extname(path) if block_given?
# Strip templating extensions as long as Tilt knows them
path = path.sub(/#{::Regexp.escape(File.extname(path))}$/, '')
end
yield File.extname(path) if block_given?
path
end
# Removes the templating extensions, while keeping the others
# @param [String] path
# @return [String]
Contract String => String
def remove_templating_extensions(path)
step_through_extensions(path)
end
# Removes the templating extensions, while keeping the others
# @param [String] path
# @return [String]
Contract String => ArrayOf[String]
def collect_extensions(path)
result = []
step_through_extensions(path) { |e| result << e }
result
end
# Convert a path to a file resprentation.
#
# @param [Pathname] path The path.
# @return [Middleman::SourceFile]
Contract Pathname, Pathname, Symbol => IsA['Middleman::SourceFile']
def path_to_source_file(path, directory, type)
types = Set.new([type])
relative_path = path.relative_path_from(directory)
# destination_dir = @options.fetch(:destination_dir, false)
# relative_path = File.join(destination_dir, relative_path) if destination_dir
::Middleman::SourceFile.new(Pathname(relative_path), path, directory, types)
end
# Finds files which should also be considered to be dirty when
# the given file(s) are touched.
#
# @param [Middleman::Application] app The app.
# @param [Pathname] files The original touched file paths.
# @return [Middleman::SourceFile] All related file paths, not including the source file paths.
Contract IsA['Middleman::Application'], ArrayOf[Pathname] => ArrayOf[IsA['Middleman::SourceFile']]
def find_related_files(app, files)
all_extensions = files.flat_map { |f| collect_extensions(f.to_s) }
sass_type_aliasing = ['.scss', '.sass']
erb_type_aliasing = ['.erb', '.haml', '.slim']
if (all_extensions & sass_type_aliasing).length > 0
all_extensions |= sass_type_aliasing
end
if (all_extensions & erb_type_aliasing).length > 0
all_extensions |= erb_type_aliasing
end
all_extensions.uniq!
app.sitemap.resources.select { |r|
local_extensions = collect_extensions(r.file_descriptor[:relative_path].to_s)
if (local_extensions & sass_type_aliasing).length > 0
local_extensions |= sass_type_aliasing
end
if (local_extensions & erb_type_aliasing).length > 0
local_extensions |= erb_type_aliasing
end
local_extensions.uniq!
((all_extensions & local_extensions).length > 0) && files.none? { |f| f == r.file_descriptor[:full_path] }
}.map(&:file_descriptor)
end
# Handy methods for dealing with URI templates. Mix into whatever class. # Handy methods for dealing with URI templates. Mix into whatever class.
module UriTemplates module UriTemplates
module_function module_function

View file

@ -141,4 +141,44 @@ describe Middleman::Util do
end end
describe "::find_related_files" do
after(:each) do
Given.cleanup!
end
before(:each) do
Given.fixture 'related-files-app'
@mm = Middleman::Application.new
end
def source_file(path)
Pathname(File.expand_path("source/#{path}"))
end
it "Finds partials possibly related to ERb files" do
related = Middleman::Util.find_related_files(@mm, [source_file('partials/_test.erb')]).map { |f| f[:full_path].to_s }
expect(related).to include File.expand_path("source/index.html.erb")
related = Middleman::Util.find_related_files(@mm, [source_file('partials/_test2.haml')]).map { |f| f[:full_path].to_s }
expect(related).to include File.expand_path("source/index.html.erb")
end
it "Finds partials possible related to Scss files" do
related = Middleman::Util.find_related_files(@mm, [source_file('stylesheets/_include4.scss')]).map { |f| f[:full_path].to_s }
expect(related).to include File.expand_path("source/stylesheets/site.css.scss")
expect(related).to include File.expand_path("source/stylesheets/include2.css.scss")
related = Middleman::Util.find_related_files(@mm, [source_file('stylesheets/include2.css.scss')]).map { |f| f[:full_path].to_s }
expect(related).to include File.expand_path("source/stylesheets/site.css.scss")
expect(related).not_to include File.expand_path("source/stylesheets/include2.css.scss")
related = Middleman::Util.find_related_files(@mm, [source_file('stylesheets/include1.css')]).map { |f| f[:full_path].to_s }
expect(related).to include File.expand_path("source/stylesheets/site.css.scss")
expect(related).to include File.expand_path("source/stylesheets/include2.css.scss")
related = Middleman::Util.find_related_files(@mm, [source_file('stylesheets/_include3.sass')]).map { |f| f[:full_path].to_s }
expect(related).to include File.expand_path("source/stylesheets/site.css.scss")
expect(related).to include File.expand_path("source/stylesheets/include2.css.scss")
end
end
end end