Add system-wide file read cache
This commit is contained in:
parent
827d5fbb1d
commit
ffa662a917
|
@ -13,5 +13,3 @@ Feature: Import files
|
|||
Then I should see 'jQuery'
|
||||
When I go to "/bower_components2/jquery/dist/jquery.js"
|
||||
Then I should see 'jQuery'
|
||||
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ class NeighborFrontmatter < ::Middleman::Extension
|
|||
|
||||
next unless file
|
||||
|
||||
fmdata = ::Middleman::Util::Data.parse(file[:full_path], app.config[:frontmatter_delims], :yaml).first
|
||||
fmdata = ::Middleman::Util::Data.parse(file, app.config[:frontmatter_delims], :yaml).first
|
||||
opts = fmdata.extract!(:layout, :layout_engine, :renderer_options, :directory_index, :content_type)
|
||||
opts[:renderer_options].symbolize_keys! if opts.key?(:renderer_options)
|
||||
ignored = fmdata.delete(:ignored)
|
||||
|
|
|
@ -26,7 +26,7 @@ class NeighborFrontmatter < ::Middleman::Extension
|
|||
end
|
||||
|
||||
def apply_neighbor_data(resource, file)
|
||||
fmdata = ::Middleman::Util::Data.parse(file[:full_path], app.config[:frontmatter_delims], :yaml).first
|
||||
fmdata = ::Middleman::Util::Data.parse(file, app.config[:frontmatter_delims], :yaml).first
|
||||
opts = fmdata.extract!(:layout, :layout_engine, :renderer_options, :directory_index, :content_type)
|
||||
opts[:renderer_options].symbolize_keys! if opts.key?(:renderer_options)
|
||||
ignored = fmdata.delete(:ignored)
|
||||
|
|
|
@ -100,10 +100,10 @@ module Middleman
|
|||
basename = File.basename(data_path, extension)
|
||||
|
||||
if %w(.yaml .yml).include?(extension)
|
||||
data, postscript = ::Middleman::Util::Data.parse(file[:full_path], @app.config[:frontmatter_delims], :yaml)
|
||||
data, postscript = ::Middleman::Util::Data.parse(file, @app.config[:frontmatter_delims], :yaml)
|
||||
data[:postscript] = postscript if !postscript.nil? && data.is_a?(Hash)
|
||||
elsif extension == '.json'
|
||||
data, _postscript = ::Middleman::Util::Data.parse(file[:full_path], @app.config[:frontmatter_delims], :json)
|
||||
data, _postscript = ::Middleman::Util::Data.parse(file, @app.config[:frontmatter_delims], :json)
|
||||
else
|
||||
return
|
||||
end
|
||||
|
|
|
@ -72,8 +72,10 @@ module Middleman::CoreExtensions
|
|||
|
||||
return [{}, nil] unless file
|
||||
|
||||
@cache[file[:full_path]] ||= ::Middleman::Util::Data.parse(
|
||||
file[:full_path],
|
||||
return @cache[file[:full_path]] if @cache.key?(file[:full_path])
|
||||
|
||||
@cache[file[:full_path]] = ::Middleman::Util::Data.parse(
|
||||
file,
|
||||
app.config[:frontmatter_delims]
|
||||
)
|
||||
end
|
||||
|
|
|
@ -97,11 +97,12 @@ module Middleman
|
|||
Contract String
|
||||
def template_data_for_file
|
||||
if @app.extensions[:front_matter]
|
||||
@app.extensions[:front_matter].template_data_for_file(@path) || File.read(@path)
|
||||
else
|
||||
file = @app.files.find(:source, @path)
|
||||
file.read if file
|
||||
result = @app.extensions[:front_matter].template_data_for_file(@path)
|
||||
return result unless result.nil?
|
||||
end
|
||||
|
||||
file = @app.files.find(:source, @path)
|
||||
file ? file.read : File.read(@path)
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -235,8 +235,8 @@ module Middleman
|
|||
|
||||
if ssl_certificate || ssl_private_key
|
||||
raise 'You must provide both :ssl_certificate and :ssl_private_key' unless ssl_private_key && ssl_certificate
|
||||
http_opts[:SSLCertificate] = OpenSSL::X509::Certificate.new File.read ssl_certificate
|
||||
http_opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new File.read ssl_private_key
|
||||
http_opts[:SSLCertificate] = OpenSSL::X509::Certificate.new ::File.read ssl_certificate
|
||||
http_opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new ::File.read ssl_private_key
|
||||
else
|
||||
# use a generated self-signed cert
|
||||
http_opts[:SSLCertName] = [
|
||||
|
|
|
@ -14,7 +14,7 @@ module Middleman
|
|||
def read_template_file(template_path, _)
|
||||
file = app.files.find(:source, "_#{template_path}.liquid")
|
||||
raise ::Liquid::FileSystemError, "No such template '#{template_path}'" unless file
|
||||
File.read(file[:full_path])
|
||||
file.read
|
||||
end
|
||||
|
||||
# @return Array<Middleman::Sitemap::Resource>
|
||||
|
|
|
@ -54,7 +54,7 @@ module Middleman
|
|||
source = Pathname(source) if source && source.is_a?(String)
|
||||
|
||||
@file_descriptor = if source && source.is_a?(Pathname)
|
||||
::Middleman::SourceFile.new(source.relative_path_from(@app.source_dir), source, @app.source_dir, Set.new([:source]))
|
||||
::Middleman::SourceFile.new(source.relative_path_from(@app.source_dir), source, @app.source_dir, Set.new([:source]), 0)
|
||||
else
|
||||
source
|
||||
end
|
||||
|
|
|
@ -3,7 +3,12 @@ require 'middleman-core/contracts'
|
|||
|
||||
module Middleman
|
||||
# The standard "record" that contains information about a file on disk.
|
||||
SourceFile = Struct.new :relative_path, :full_path, :directory, :types
|
||||
SourceFile = Struct.new(:relative_path, :full_path, :directory, :types, :version) do
|
||||
def read
|
||||
::Middleman::Sources.file_cache[full_path] ||= {}
|
||||
::Middleman::Sources.file_cache[full_path][version] ||= ::File.read(full_path)
|
||||
end
|
||||
end
|
||||
|
||||
# Sources handle multiple on-disk collections of files which make up
|
||||
# a Middleman project. They are separated by `type` which can then be
|
||||
|
@ -36,6 +41,8 @@ module Middleman
|
|||
# Reference to the global logger.
|
||||
def_delegator :@app, :logger
|
||||
|
||||
cattr_accessor :file_cache
|
||||
|
||||
# Built-in types
|
||||
# :source, :data, :locales, :reload
|
||||
|
||||
|
@ -50,6 +57,8 @@ module Middleman
|
|||
@watchers = watchers
|
||||
@sorted_watchers = @watchers.dup.freeze
|
||||
|
||||
::Middleman::Sources.file_cache = {}
|
||||
|
||||
@options = options
|
||||
|
||||
# Set of procs wanting to be notified of changes
|
||||
|
@ -177,9 +186,10 @@ module Middleman
|
|||
# @return [Middleman::SourceFile, nil]
|
||||
Contract Or[Symbol, ArrayOf[Symbol], SetOf[Symbol]], String, Maybe[Bool] => Maybe[SourceFile]
|
||||
def find(types, path, glob=false)
|
||||
array_of_types = Array(types)
|
||||
|
||||
watchers
|
||||
.lazy
|
||||
.select { |d| Array(types).include?(d.type) }
|
||||
.select { |d| array_of_types.include?(d.type) }
|
||||
.map { |d| d.find(path, glob) }
|
||||
.reject(&:nil?)
|
||||
.first
|
||||
|
@ -193,7 +203,6 @@ module Middleman
|
|||
Contract Or[Symbol, ArrayOf[Symbol], SetOf[Symbol]], String => Bool
|
||||
def exists?(types, path)
|
||||
watchers
|
||||
.lazy
|
||||
.select { |d| Array(types).include?(d.type) }
|
||||
.any? { |d| d.exists?(path) }
|
||||
end
|
||||
|
|
|
@ -228,11 +228,11 @@ module Middleman
|
|||
Contract ArrayOf[Pathname], ArrayOf[Pathname] => Any
|
||||
def update(updated_paths, removed_paths)
|
||||
valid_updates = updated_paths
|
||||
.map { |p| ::Middleman::Util.path_to_source_file(p, @directory, @type, @options.fetch(:destination_dir, false)) }
|
||||
.map { |p| @files[p] || path_to_source_file(p, @directory, @type, @options[:destination_dir]) }
|
||||
.select(&method(:valid?))
|
||||
|
||||
valid_updates.each do |f|
|
||||
add_file_to_cache(f)
|
||||
record_file_change(f)
|
||||
logger.debug "== Change (#{f[:types].inspect}): #{f[:relative_path]}"
|
||||
end
|
||||
|
||||
|
@ -245,11 +245,9 @@ module Middleman
|
|||
valid_updates |= related_updates
|
||||
|
||||
valid_removes = removed_paths
|
||||
.lazy
|
||||
.select(&@files.method(:key?))
|
||||
.map(&@files.method(:[]))
|
||||
.select(&method(:valid?))
|
||||
.to_a
|
||||
.each do |f|
|
||||
remove_file_from_cache(f)
|
||||
logger.debug "== Deletion (#{f[:types].inspect}): #{f[:relative_path]}"
|
||||
|
@ -262,11 +260,29 @@ module Middleman
|
|||
]) unless valid_updates.empty? && valid_removes.empty?
|
||||
end
|
||||
|
||||
# Convert a path to a file resprentation.
|
||||
#
|
||||
# @param [Pathname] path The path.
|
||||
# @return [Middleman::SourceFile]
|
||||
Contract Pathname, Pathname, Symbol, Maybe[String] => ::Middleman::SourceFile
|
||||
def path_to_source_file(path, directory, type, destination_dir)
|
||||
types = Set.new([type])
|
||||
|
||||
relative_path = path.relative_path_from(directory)
|
||||
relative_path = File.join(destination_dir, relative_path) if destination_dir
|
||||
|
||||
::Middleman::SourceFile.new(Pathname(relative_path), path, directory, types, 0)
|
||||
end
|
||||
|
||||
Contract IsA['Middleman::SourceFile'] => Any
|
||||
def add_file_to_cache(f)
|
||||
def record_file_change(f)
|
||||
if @files[f[:full_path]]
|
||||
@files[f[:full_path]][:version] += 1
|
||||
else
|
||||
@files[f[:full_path]] = f
|
||||
@extensionless_files[strip_extensions(f[:full_path])] = f
|
||||
end
|
||||
end
|
||||
|
||||
Contract IsA['Middleman::SourceFile'] => Any
|
||||
def remove_file_from_cache(f)
|
||||
|
|
|
@ -110,7 +110,7 @@ module Middleman
|
|||
r = sitemap.find_resource_by_path(source_path)
|
||||
|
||||
if (r && !r.template?) || (Tilt[partial_file[:full_path]].nil? && partial_file[:full_path].exist?)
|
||||
File.read(partial_file[:full_path])
|
||||
partial_file.read
|
||||
else
|
||||
opts = options.dup
|
||||
locs = opts.delete(:locals)
|
||||
|
|
|
@ -35,14 +35,20 @@ module Middleman
|
|||
# @return [Boolean]
|
||||
Contract Or[String, Pathname] => Bool
|
||||
def binary?(filename)
|
||||
@@binary_cache ||= {}
|
||||
|
||||
return @@binary_cache[filename] if @@binary_cache.key?(filename)
|
||||
|
||||
@@binary_cache[filename] = begin
|
||||
path = Pathname(filename)
|
||||
ext = path.extname
|
||||
|
||||
# We hardcode detecting of gzipped SVG files
|
||||
return true if ext == '.svgz'
|
||||
|
||||
return false if Tilt.registered?(ext.sub('.', ''))
|
||||
|
||||
if ext == '.svgz'
|
||||
true
|
||||
elsif Tilt.registered?(ext.sub('.', ''))
|
||||
false
|
||||
else
|
||||
dot_ext = (ext.to_s[0] == '.') ? ext.dup : ".#{ext}"
|
||||
|
||||
if mime = ::Rack::Mime.mime_type(dot_ext, nil)
|
||||
|
@ -51,6 +57,8 @@ module Middleman
|
|||
file_contents_include_binary_bytes?(path.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Takes a matcher, which can be a literal string
|
||||
# or a string containing glob expressions, or a
|
||||
|
@ -479,20 +487,6 @@ module Middleman
|
|||
result
|
||||
end
|
||||
|
||||
# Convert a path to a file resprentation.
|
||||
#
|
||||
# @param [Pathname] path The path.
|
||||
# @return [Middleman::SourceFile]
|
||||
Contract Pathname, Pathname, Symbol, Bool => ::Middleman::SourceFile
|
||||
def path_to_source_file(path, directory, type, destination_dir)
|
||||
types = Set.new([type])
|
||||
|
||||
relative_path = path.relative_path_from(directory)
|
||||
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.
|
||||
#
|
||||
|
|
|
@ -15,13 +15,14 @@ module Middleman
|
|||
# Get the frontmatter and plain content from a file
|
||||
# @param [String] path
|
||||
# @return [Array<Hash, String>]
|
||||
Contract Pathname, Maybe[Symbol] => [Hash, Maybe[String]]
|
||||
def parse(full_path, frontmatter_delims, known_type=nil)
|
||||
Contract IsA['Middleman::SourceFile'], Maybe[Symbol] => [Hash, Maybe[String]]
|
||||
def parse(file, frontmatter_delims, known_type=nil)
|
||||
full_path = file[:full_path]
|
||||
return [{}, nil] if ::Middleman::Util.binary?(full_path)
|
||||
|
||||
# Avoid weird race condition when a file is renamed
|
||||
begin
|
||||
content = File.read(full_path)
|
||||
content = file.read
|
||||
rescue EOFError, IOError, Errno::ENOENT
|
||||
return [{}, nil]
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue