mirror of
https://github.com/middleman/middleman.git
synced 2022-11-09 12:20:27 -05:00
Avoid all the Maybe nonsense (#2224)
Rename dep classes Split into files Sass was still returning nil Let's make dependencies immutable Avoid IsA Fix contract
This commit is contained in:
parent
0b03aa358a
commit
067dd350c8
16 changed files with 373 additions and 299 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -23,4 +23,5 @@ Makefile
|
|||
.idea
|
||||
*.sublime-workspace
|
||||
/bin
|
||||
/vendor/bundle/
|
||||
/vendor/bundle/
|
||||
deps.yml
|
|
@ -2,7 +2,9 @@ require 'pathname'
|
|||
require 'fileutils'
|
||||
require 'tempfile'
|
||||
require 'parallel'
|
||||
require 'middleman-core/dependencies'
|
||||
require 'middleman-core/dependencies/graph'
|
||||
require 'middleman-core/dependencies/edge'
|
||||
require 'middleman-core/dependencies/vertices/file_vertex'
|
||||
require 'middleman-core/callback_manager'
|
||||
require 'middleman-core/contracts'
|
||||
|
||||
|
@ -81,8 +83,7 @@ module Middleman
|
|||
prerender_css.tap do |resources|
|
||||
if @track_dependencies
|
||||
resources.each do |r|
|
||||
dependency = r[1]
|
||||
@graph.add_dependency(dependency) unless dependency.nil?
|
||||
@graph.add_edge(r[1]) unless r[1].nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -94,8 +95,7 @@ module Middleman
|
|||
output_files.tap do |resources|
|
||||
if @track_dependencies
|
||||
resources.each do |r|
|
||||
dependency = r[1]
|
||||
@graph.add_dependency(dependency) unless dependency.nil?
|
||||
@graph.add_edge(r[1]) unless r[1].nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -120,7 +120,7 @@ module Middleman
|
|||
|
||||
# Pre-request CSS to give Compass a chance to build sprites
|
||||
# @return [Array<Resource>] List of css resources that were output.
|
||||
Contract ArrayOf[[Pathname, Maybe[::Middleman::Dependencies::DependencyLink]]]
|
||||
Contract ArrayOf[[Pathname, Maybe[::Middleman::Dependencies::Edge]]]
|
||||
def prerender_css
|
||||
logger.debug '== Prerendering CSS'
|
||||
|
||||
|
@ -143,7 +143,7 @@ module Middleman
|
|||
|
||||
# Find all the files we need to output and do so.
|
||||
# @return [Array<Resource>] List of resources that were output.
|
||||
Contract ArrayOf[[Pathname, Maybe[::Middleman::Dependencies::DependencyLink]]]
|
||||
Contract ArrayOf[[Pathname, Maybe[::Middleman::Dependencies::Edge]]]
|
||||
def output_files
|
||||
logger.debug '== Building files'
|
||||
|
||||
|
@ -155,7 +155,7 @@ module Middleman
|
|||
output_resources(resources.to_a)
|
||||
end
|
||||
|
||||
Contract OldResourceList => ArrayOf[Or[Bool, [Pathname, Maybe[::Middleman::Dependencies::DependencyLink]]]]
|
||||
Contract OldResourceList => ArrayOf[Or[Bool, [Pathname, Maybe[::Middleman::Dependencies::Edge]]]]
|
||||
def output_resources(resources)
|
||||
res_count = resources.count
|
||||
|
||||
|
@ -278,7 +278,7 @@ module Middleman
|
|||
# Try to output a resource and capture errors.
|
||||
# @param [Middleman::Sitemap::Resource] resource The resource.
|
||||
# @return [void]
|
||||
Contract IsA['Middleman::Sitemap::Resource'] => Or[Bool, [Pathname, Maybe[::Middleman::Dependencies::DependencyLink]]]
|
||||
Contract IsA['Middleman::Sitemap::Resource'] => Or[Bool, [Pathname, Maybe[::Middleman::Dependencies::Edge]]]
|
||||
def output_resource(resource)
|
||||
::Middleman::Util.instrument 'builder.output.resource', path: File.basename(resource.destination_path) do
|
||||
begin
|
||||
|
@ -305,17 +305,17 @@ module Middleman
|
|||
end
|
||||
end
|
||||
|
||||
deps = nil
|
||||
vertices = nil
|
||||
|
||||
if resource.binary?
|
||||
export_file!(output_file, resource.file_descriptor[:full_path], true)
|
||||
else
|
||||
content = resource.render({}, {})
|
||||
|
||||
unless resource.dependencies.empty?
|
||||
deps = ::Middleman::Dependencies::DependencyLink.new(
|
||||
::Middleman::Dependencies::FileDependency.from_resource(resource),
|
||||
resource.dependencies
|
||||
unless resource.vertices.empty?
|
||||
vertices = ::Middleman::Dependencies::Edge.new(
|
||||
::Middleman::Dependencies::FileVertex.from_resource(resource),
|
||||
resource.vertices
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -326,7 +326,7 @@ module Middleman
|
|||
return false
|
||||
end
|
||||
|
||||
[output_file, deps]
|
||||
[output_file, vertices]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,6 +19,29 @@ if ENV['CONTRACTS'] != 'false'
|
|||
end
|
||||
|
||||
VectorOf = Contracts::CollectionOf::Factory.new(::Hamster::Vector)
|
||||
|
||||
class ImmutableHashOf < Contracts::CallableClass
|
||||
INVALID_KEY_VALUE_PAIR = 'You should provide only one key-value pair to HashOf contract'.freeze
|
||||
|
||||
def initialize(key, value)
|
||||
@key = key
|
||||
@value = value
|
||||
end
|
||||
|
||||
def valid?(hash)
|
||||
return false unless hash.is_a?(::Hamster::Hash)
|
||||
|
||||
keys_match = hash.keys.map { |k| Contract.valid?(k, @key) }.all?
|
||||
vals_match = hash.values.map { |v| Contract.valid?(v, @value) }.all?
|
||||
|
||||
[keys_match, vals_match].all?
|
||||
end
|
||||
|
||||
def to_s
|
||||
"ImmutableHash<#{@key}, #{@value}>"
|
||||
end
|
||||
end
|
||||
|
||||
ImmutableSetOf = Contracts::CollectionOf::Factory.new(::Hamster::Set)
|
||||
ImmutableSortedSetOf = Contracts::CollectionOf::Factory.new(::Hamster::SortedSet)
|
||||
OldResourceList = Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']]
|
||||
|
@ -110,6 +133,9 @@ else
|
|||
class VectorOf < Callable
|
||||
end
|
||||
|
||||
class ImmutableHashOf < Callable
|
||||
end
|
||||
|
||||
class ImmutableSetOf < Callable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,261 +1,47 @@
|
|||
require 'middleman-core/contracts'
|
||||
require 'set'
|
||||
require 'pathname'
|
||||
require 'digest/sha1'
|
||||
require 'yaml'
|
||||
require 'middleman-core/contracts'
|
||||
require 'middleman-core/dependencies/graph'
|
||||
require 'middleman-core/dependencies/vertices'
|
||||
|
||||
module Middleman
|
||||
module Dependencies
|
||||
class BaseDependency
|
||||
include Contracts
|
||||
|
||||
DEPENDENCY_KEY = Or[String, Symbol]
|
||||
DEPENDENCY_ATTRS = HashOf[Symbol, String]
|
||||
SERIALIZED_DEPENDENCY = {
|
||||
key: Any, # Weird inheritance bug
|
||||
type: Symbol,
|
||||
attributes: DEPENDENCY_ATTRS
|
||||
}.freeze
|
||||
|
||||
Contract DEPENDENCY_KEY
|
||||
attr_reader :key
|
||||
|
||||
Contract DEPENDENCY_ATTRS
|
||||
attr_reader :attributes
|
||||
|
||||
Contract DEPENDENCY_KEY, DEPENDENCY_ATTRS => Any
|
||||
def initialize(key, attributes)
|
||||
@key = key
|
||||
@attributes = attributes
|
||||
end
|
||||
|
||||
Contract BaseDependency => Bool
|
||||
def ==(other)
|
||||
key == other.key
|
||||
end
|
||||
|
||||
Contract Bool
|
||||
def valid?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
Contract IsA['Middleman::Sitemap::Resource'] => Bool
|
||||
def invalidates_resource?(_resource)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
Contract Maybe[DEPENDENCY_ATTRS] => SERIALIZED_DEPENDENCY
|
||||
def serialize(attributes = {})
|
||||
{
|
||||
key: @key,
|
||||
type: type_id,
|
||||
attributes: @attributes.merge(attributes)
|
||||
}
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
Contract Symbol
|
||||
def type_id
|
||||
self.class.const_get :TYPE_ID
|
||||
end
|
||||
|
||||
Contract Pathname, String => String
|
||||
def relative_path(root, file)
|
||||
Pathname(File.expand_path(file)).relative_path_from(root).to_s
|
||||
end
|
||||
|
||||
Contract Pathname, String => String
|
||||
def full_path(root, file)
|
||||
File.expand_path(file, root)
|
||||
end
|
||||
end
|
||||
|
||||
class FileDependency < BaseDependency
|
||||
include Contracts
|
||||
|
||||
TYPE_ID = :file
|
||||
|
||||
Contract IsA['::Middleman::Application'], String, BaseDependency::DEPENDENCY_ATTRS => FileDependency
|
||||
def self.deserialize(app, key, attributes)
|
||||
FileDependency.new(app.root_path, key, attributes)
|
||||
end
|
||||
|
||||
Contract IsA['Middleman::Sitemap::Resource'] => FileDependency
|
||||
def self.from_resource(resource)
|
||||
from_source_file(resource.app, resource.file_descriptor)
|
||||
end
|
||||
|
||||
Contract IsA['::Middleman::Application'], IsA['::Middleman::SourceFile'] => FileDependency
|
||||
def self.from_source_file(app, source_file)
|
||||
FileDependency.new(app.root_path, source_file[:full_path].to_s)
|
||||
end
|
||||
|
||||
Contract Or[String, Pathname], String, Maybe[DEPENDENCY_ATTRS] => Any
|
||||
def initialize(root, key, attributes = {})
|
||||
@root = Pathname(root)
|
||||
@full_path = full_path(@root, key)
|
||||
super(relative_path(@root, key), attributes)
|
||||
end
|
||||
|
||||
Contract Bool
|
||||
def valid?
|
||||
@is_valid = (previous_hash.nil? || hash_file == previous_hash) if @is_valid.nil?
|
||||
@is_valid
|
||||
end
|
||||
|
||||
Contract IsA['Middleman::Sitemap::Resource'] => Bool
|
||||
def invalidates_resource?(resource)
|
||||
resource.file_descriptor[:full_path].to_s == @full_path
|
||||
end
|
||||
|
||||
Contract BaseDependency::SERIALIZED_DEPENDENCY
|
||||
def serialize
|
||||
super({
|
||||
hash: valid? && !previous_hash.nil? ? previous_hash : hash_file
|
||||
})
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
Contract Maybe[String]
|
||||
def previous_hash
|
||||
@attributes[:hash]
|
||||
end
|
||||
|
||||
Contract String
|
||||
def hash_file
|
||||
::Digest::SHA1.file(@full_path).hexdigest
|
||||
end
|
||||
end
|
||||
|
||||
DEPENDENCY_CLASSES_BY_TYPE = {
|
||||
FileDependency::TYPE_ID => FileDependency
|
||||
}.freeze
|
||||
|
||||
class DependencyLink
|
||||
include Contracts
|
||||
|
||||
Contract BaseDependency
|
||||
attr_reader :dependency
|
||||
|
||||
Contract Maybe[SetOf[BaseDependency]]
|
||||
attr_accessor :depends_on
|
||||
|
||||
Contract BaseDependency, Maybe[SetOf[BaseDependency]] => Any
|
||||
def initialize(dependency, depends_on = nil)
|
||||
@dependency = dependency
|
||||
@depends_on = nil
|
||||
@depends_on = Set.new(depends_on) unless depends_on.nil?
|
||||
end
|
||||
|
||||
Contract String
|
||||
def to_s
|
||||
"#<#{self.class} file=#{@dependency} depends_on=#{@depends_on}>"
|
||||
end
|
||||
end
|
||||
|
||||
class Graph
|
||||
include Contracts
|
||||
|
||||
Contract HashOf[BaseDependency::DEPENDENCY_KEY, BaseDependency]
|
||||
attr_reader :dependencies
|
||||
|
||||
Contract HashOf[BaseDependency, SetOf[BaseDependency]]
|
||||
attr_accessor :dependency_map
|
||||
|
||||
def initialize(dependencies = {})
|
||||
@dependencies = dependencies
|
||||
@dependency_map = {}
|
||||
end
|
||||
|
||||
Contract BaseDependency => BaseDependency
|
||||
def known_dependency_or_new(dep)
|
||||
@dependencies[dep.key] ||= dep
|
||||
end
|
||||
|
||||
Contract DependencyLink => Any
|
||||
def add_dependency(link)
|
||||
deduped_depender = known_dependency_or_new link.dependency
|
||||
|
||||
@dependency_map[deduped_depender] ||= Set.new
|
||||
@dependency_map[deduped_depender] << deduped_depender
|
||||
|
||||
return if link.depends_on.nil?
|
||||
|
||||
link.depends_on.each do |depended_on|
|
||||
deduped_depended_on = known_dependency_or_new depended_on
|
||||
|
||||
@dependency_map[deduped_depended_on] ||= Set.new
|
||||
@dependency_map[deduped_depended_on] << deduped_depended_on
|
||||
@dependency_map[deduped_depended_on] << deduped_depender
|
||||
end
|
||||
end
|
||||
|
||||
Contract String => Bool
|
||||
def exists?(file_path)
|
||||
@dependency_map.key?(file_path)
|
||||
end
|
||||
|
||||
Contract SetOf[BaseDependency]
|
||||
def invalidated
|
||||
@_invalidated_cache ||= begin
|
||||
invalidated_dependencies = @dependency_map.keys.select do |dependency|
|
||||
# Either "Missing from known dependencies"
|
||||
# Or invalided by the class
|
||||
!@dependencies.key?(dependency.key) || !dependency.valid?
|
||||
end
|
||||
|
||||
invalidated_dependencies.reduce(Set.new) do |sum, dependency|
|
||||
sum | invalidated_with_parents(dependency)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Contract BaseDependency => SetOf[BaseDependency]
|
||||
def invalidated_with_parents(dependency)
|
||||
# TODO, recurse more?
|
||||
(Set.new + (@dependency_map[dependency])) << dependency
|
||||
end
|
||||
|
||||
Contract IsA['::Middleman::Sitemap::Resource'] => Bool
|
||||
def invalidates_resource?(resource)
|
||||
invalidated.any? { |d| d.invalidates_resource?(resource) }
|
||||
end
|
||||
end
|
||||
|
||||
include Contracts
|
||||
|
||||
DEFAULT_FILE_PATH = 'deps.yml'.freeze
|
||||
RUBY_FILES = ['**/*.rb', 'Gemfile.lock'].freeze
|
||||
|
||||
module_function
|
||||
|
||||
Contract IsA['::Middleman::Application'], Graph => String
|
||||
def serialize(app, graph)
|
||||
ruby_files = Dir['**/*.rb', 'Gemfile.lock'].reduce([]) do |sum, file|
|
||||
ruby_files = Dir.glob(RUBY_FILES).reduce([]) do |sum, file|
|
||||
sum << {
|
||||
file: Pathname(File.expand_path(file)).relative_path_from(app.root_path).to_s,
|
||||
hash: ::Digest::SHA1.file(file).hexdigest
|
||||
}
|
||||
end
|
||||
|
||||
dependency_links = graph.dependency_map.reduce([]) do |sum, (dependency, depended_on_by)|
|
||||
edges = graph.dependency_map.reduce([]) do |sum, (vertex, depended_on_by)|
|
||||
sum << {
|
||||
key: dependency.key,
|
||||
depended_on_by: depended_on_by.delete(dependency).to_a.map(&:key).sort
|
||||
key: vertex.key,
|
||||
depended_on_by: depended_on_by.delete(vertex).to_a.map(&:key).sort
|
||||
}
|
||||
end
|
||||
|
||||
dependencies = graph.dependency_map.reduce([]) do |sum, (dependency, _depended_on_by)|
|
||||
sum << dependency.serialize
|
||||
vertices = graph.dependency_map.reduce([]) do |sum, (vertex, _depended_on_by)|
|
||||
sum << vertex.serialize
|
||||
end
|
||||
|
||||
::YAML.dump(
|
||||
ruby_files: ruby_files.sort_by { |d| d[:file] },
|
||||
dependency_links: dependency_links.sort_by { |d| d[:file] },
|
||||
dependencies: dependencies.sort_by { |d| d[:key] }
|
||||
edges: edges.sort_by { |d| d[:file] },
|
||||
vertices: vertices.sort_by { |d| d[:key] }
|
||||
)
|
||||
end
|
||||
|
||||
DEFAULT_FILE_PATH = 'deps.yml'.freeze
|
||||
|
||||
Contract IsA['::Middleman::Application'], Graph, Maybe[String] => Any
|
||||
def serialize_and_save(app, graph, file_path = DEFAULT_FILE_PATH)
|
||||
File.open(file_path, 'w') do |file|
|
||||
|
@ -302,19 +88,19 @@ module Middleman
|
|||
raise InvalidatedRubyFiles, invalidated
|
||||
end
|
||||
|
||||
dependencies = data[:dependencies].each_with_object({}) do |row, sum|
|
||||
dep_class = DEPENDENCY_CLASSES_BY_TYPE[row[:type]]
|
||||
dep = dep_class.deserialize(app, row[:key], row[:attributes])
|
||||
sum[dep.key] = dep
|
||||
vertices = data[:vertices].each_with_object({}) do |row, sum|
|
||||
vertex_class = VERTICES_BY_TYPE[row[:type]]
|
||||
vertex = vertex_class.deserialize(app, row[:key], row[:attributes])
|
||||
sum[vertex.key] = vertex
|
||||
end
|
||||
|
||||
graph = Graph.new(dependencies)
|
||||
graph = Graph.new(vertices)
|
||||
|
||||
dependency_links = data[:dependency_links]
|
||||
graph.dependency_map = dependency_links.each_with_object({}) do |row, sum|
|
||||
dependency = graph.dependencies[row[:key]]
|
||||
depended_on_by = row[:depended_on_by].map { |k| graph.dependencies[k] }
|
||||
sum[dependency] = Set.new(depended_on_by) << dependency
|
||||
edges = data[:edges]
|
||||
graph.dependency_map = edges.each_with_object({}) do |row, sum|
|
||||
vertex = graph.vertices[row[:key]]
|
||||
depended_on_by = row[:depended_on_by].map { |k| graph.vertices[k] }
|
||||
sum[vertex] = ::Hamster::Set.new(depended_on_by) << vertex
|
||||
end
|
||||
|
||||
graph
|
||||
|
|
27
middleman-core/lib/middleman-core/dependencies/edge.rb
Normal file
27
middleman-core/lib/middleman-core/dependencies/edge.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
require 'middleman-core/contracts'
|
||||
require 'middleman-core/dependencies/vertices/vertex'
|
||||
|
||||
module Middleman
|
||||
module Dependencies
|
||||
class Edge
|
||||
include Contracts
|
||||
|
||||
Contract Vertex
|
||||
attr_reader :vertex
|
||||
|
||||
Contract ImmutableSetOf[Vertex]
|
||||
attr_accessor :depends_on
|
||||
|
||||
Contract Vertex, ImmutableSetOf[Vertex] => Any
|
||||
def initialize(vertex, depends_on)
|
||||
@vertex = vertex
|
||||
@depends_on = depends_on
|
||||
end
|
||||
|
||||
Contract String
|
||||
def to_s
|
||||
"#<#{self.class} vertex=#{@vertex} edges=#{@edges}>"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
78
middleman-core/lib/middleman-core/dependencies/graph.rb
Normal file
78
middleman-core/lib/middleman-core/dependencies/graph.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
require 'set'
|
||||
require 'middleman-core/contracts'
|
||||
require 'middleman-core/dependencies/vertices/vertex'
|
||||
require 'middleman-core/dependencies/edge'
|
||||
|
||||
module Middleman
|
||||
module Dependencies
|
||||
class Graph
|
||||
include Contracts
|
||||
|
||||
Contract HashOf[Vertex::VERTEX_KEY, Vertex]
|
||||
attr_reader :vertices
|
||||
|
||||
Contract ImmutableHashOf[Vertex, ImmutableSetOf[Vertex]]
|
||||
attr_accessor :dependency_map
|
||||
|
||||
def initialize(vertices = {})
|
||||
@vertices = vertices
|
||||
@dependency_map = ::Hamster::Hash.empty
|
||||
end
|
||||
|
||||
Contract Vertex => Vertex
|
||||
def known_vertex_or_new(v)
|
||||
@vertices[v.key] ||= v
|
||||
end
|
||||
|
||||
Contract Edge => Any
|
||||
def add_edge(edge)
|
||||
deduped_vertex = known_vertex_or_new edge.vertex
|
||||
|
||||
@dependency_map.put(deduped_vertex) do |v|
|
||||
(v || ::Hamster::Set.empty) << deduped_vertex
|
||||
end
|
||||
|
||||
return if edge.depends_on.nil?
|
||||
|
||||
edge.depends_on.each do |depended_on|
|
||||
deduped_depended_on = known_vertex_or_new depended_on
|
||||
|
||||
@dependency_map.put(deduped_depended_on) do |v|
|
||||
(v || ::Hamster::Set.empty) << deduped_depended_on << deduped_vertex
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Contract String => Bool
|
||||
def exists?(file_path)
|
||||
@dependency_map.key?(file_path)
|
||||
end
|
||||
|
||||
Contract ImmutableSetOf[Vertex]
|
||||
def invalidated
|
||||
@_invalidated_cache ||= begin
|
||||
invalidated_vertices = @dependency_map.keys.select do |vertex|
|
||||
# Either "Missing from known vertices"
|
||||
# Or invalided by the class
|
||||
!@vertices.key?(vertex.key) || !vertex.valid?
|
||||
end
|
||||
|
||||
invalidated_vertices.reduce(::Hamster::Set.empty) do |sum, vertex|
|
||||
sum | invalidated_with_parents(vertex)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Contract Vertex => ImmutableSetOf[Vertex]
|
||||
def invalidated_with_parents(vertex)
|
||||
# TODO, recurse more?
|
||||
@dependency_map[vertex] << vertex
|
||||
end
|
||||
|
||||
Contract IsA['::Middleman::Sitemap::Resource'] => Bool
|
||||
def invalidates_resource?(resource)
|
||||
invalidated.any? { |d| d.invalidates_resource?(resource) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
require 'middleman-core/dependencies/vertices/file_vertex'
|
||||
|
||||
module Middleman
|
||||
module Dependencies
|
||||
VERTICES_BY_TYPE = {
|
||||
FileVertex::TYPE_ID => FileVertex
|
||||
}.freeze
|
||||
end
|
||||
end
|
|
@ -0,0 +1,66 @@
|
|||
require 'pathname'
|
||||
require 'digest/sha1'
|
||||
require 'middleman-core/contracts'
|
||||
require 'middleman-core/dependencies/vertices/vertex'
|
||||
|
||||
module Middleman
|
||||
module Dependencies
|
||||
class FileVertex < Vertex
|
||||
include Contracts
|
||||
|
||||
TYPE_ID = :file
|
||||
|
||||
Contract IsA['::Middleman::Application'], String, Vertex::VERTEX_ATTRS => FileVertex
|
||||
def self.deserialize(app, key, attributes)
|
||||
FileVertex.new(app.root_path, key, attributes)
|
||||
end
|
||||
|
||||
Contract IsA['Middleman::Sitemap::Resource'] => FileVertex
|
||||
def self.from_resource(resource)
|
||||
from_source_file(resource.app, resource.file_descriptor)
|
||||
end
|
||||
|
||||
Contract IsA['::Middleman::Application'], IsA['::Middleman::SourceFile'] => FileVertex
|
||||
def self.from_source_file(app, source_file)
|
||||
FileVertex.new(app.root_path, source_file[:full_path].to_s)
|
||||
end
|
||||
|
||||
Contract Or[String, Pathname], String, Maybe[VERTEX_ATTRS] => Any
|
||||
def initialize(root, key, attributes = {})
|
||||
@root = Pathname(root)
|
||||
@full_path = full_path(@root, key)
|
||||
super(relative_path(@root, key), attributes)
|
||||
end
|
||||
|
||||
Contract Bool
|
||||
def valid?
|
||||
@is_valid = (previous_hash.nil? || hash_file == previous_hash) if @is_valid.nil?
|
||||
@is_valid
|
||||
end
|
||||
|
||||
Contract IsA['Middleman::Sitemap::Resource'] => Bool
|
||||
def invalidates_resource?(resource)
|
||||
resource.file_descriptor[:full_path].to_s == @full_path
|
||||
end
|
||||
|
||||
Contract Vertex::SERIALIZED_VERTEX
|
||||
def serialize
|
||||
super({
|
||||
hash: valid? && !previous_hash.nil? ? previous_hash : hash_file
|
||||
})
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
Contract Maybe[String]
|
||||
def previous_hash
|
||||
@attributes[:hash]
|
||||
end
|
||||
|
||||
Contract String
|
||||
def hash_file
|
||||
::Digest::SHA1.file(@full_path).hexdigest
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,71 @@
|
|||
require 'pathname'
|
||||
require 'middleman-core/contracts'
|
||||
|
||||
module Middleman
|
||||
module Dependencies
|
||||
class Vertex
|
||||
include Contracts
|
||||
|
||||
VERTEX_KEY = Or[String, Symbol]
|
||||
VERTEX_ATTRS = HashOf[Symbol, String]
|
||||
SERIALIZED_VERTEX = {
|
||||
key: Any, # Weird inheritance bug
|
||||
type: Symbol,
|
||||
attributes: VERTEX_ATTRS
|
||||
}.freeze
|
||||
|
||||
Contract VERTEX_KEY
|
||||
attr_reader :key
|
||||
|
||||
Contract VERTEX_ATTRS
|
||||
attr_reader :attributes
|
||||
|
||||
Contract VERTEX_KEY, VERTEX_ATTRS => Any
|
||||
def initialize(key, attributes)
|
||||
@key = key
|
||||
@attributes = attributes
|
||||
end
|
||||
|
||||
Contract Vertex => Bool
|
||||
def ==(other)
|
||||
key == other.key
|
||||
end
|
||||
|
||||
Contract Bool
|
||||
def valid?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
Contract IsA['Middleman::Sitemap::Resource'] => Bool
|
||||
def invalidates_resource?(_resource)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
Contract Maybe[VERTEX_ATTRS] => SERIALIZED_VERTEX
|
||||
def serialize(attributes = {})
|
||||
{
|
||||
key: @key,
|
||||
type: type_id,
|
||||
attributes: @attributes.merge(attributes)
|
||||
}
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
Contract Symbol
|
||||
def type_id
|
||||
self.class.const_get :TYPE_ID
|
||||
end
|
||||
|
||||
Contract Pathname, String => String
|
||||
def relative_path(root, file)
|
||||
Pathname(File.expand_path(file)).relative_path_from(root).to_s
|
||||
end
|
||||
|
||||
Contract Pathname, String => String
|
||||
def full_path(root, file)
|
||||
File.expand_path(file, root)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,8 +1,9 @@
|
|||
require 'tilt'
|
||||
require 'set'
|
||||
require 'hamster'
|
||||
require 'active_support/core_ext/string/output_safety'
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
require 'middleman-core/contracts'
|
||||
require 'middleman-core/dependencies/vertices/vertex'
|
||||
|
||||
::Tilt.default_mapping.lazy_map.delete('html')
|
||||
::Tilt.default_mapping.lazy_map.delete('csv')
|
||||
|
@ -16,15 +17,15 @@ module Middleman
|
|||
@_cache ||= ::Tilt::Cache.new
|
||||
end
|
||||
|
||||
Contract Maybe[SetOf[IsA['::Middleman::Dependencies::BaseDependency']]]
|
||||
attr_reader :dependencies
|
||||
Contract ImmutableSetOf[::Middleman::Dependencies::Vertex]
|
||||
attr_reader :vertices
|
||||
|
||||
def_delegator :"self.class", :cache
|
||||
|
||||
def initialize(app, path)
|
||||
@app = app
|
||||
@path = path.to_s
|
||||
@dependencies = nil
|
||||
@vertices = ::Hamster::Set.empty
|
||||
end
|
||||
|
||||
# Render an on-disk file. Used for everything, including layouts.
|
||||
|
@ -71,12 +72,12 @@ module Middleman
|
|||
# ::Tilt.new(path, 1, options) { body }
|
||||
# end
|
||||
|
||||
@dependencies = nil
|
||||
@vertices = ::Hamster::Set.empty
|
||||
|
||||
# Render using Tilt
|
||||
content = ::Middleman::Util.instrument 'render.tilt', path: path do
|
||||
template.render(context, locs, &block).tap do
|
||||
@dependencies = template.dependencies if template.respond_to?(:dependencies)
|
||||
@vertices = template.vertices if template.respond_to?(:vertices)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
require 'hamster'
|
||||
require 'middleman-core/contracts'
|
||||
require 'middleman-core/dependencies/vertices/vertex'
|
||||
|
||||
module Middleman
|
||||
class Filter
|
||||
|
@ -31,7 +33,7 @@ module Middleman
|
|||
@after_filter = @options.fetch(:after_filter, nil)
|
||||
end
|
||||
|
||||
Contract String => String
|
||||
Contract String => [String, ImmutableSetOf[::Middleman::Dependencies::Vertex]]
|
||||
def execute_filter(_body)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
@ -45,10 +47,10 @@ module Middleman
|
|||
@callable = callable
|
||||
end
|
||||
|
||||
Contract String => [String, Maybe[SetOf[String]]]
|
||||
Contract String => [String, ImmutableSetOf[::Middleman::Dependencies::Vertex]]
|
||||
def execute_filter(body)
|
||||
result = @callable.call(body)
|
||||
result.is_a?(Array) ? result : [result, nil]
|
||||
result.is_a?(Array) ? result : [result, ::Hamster::Set.empty]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
require 'hamster'
|
||||
require 'middleman-core/util'
|
||||
require 'middleman-core/filter'
|
||||
require 'middleman-core/contracts'
|
||||
require 'middleman-core/dependencies/vertices/vertex'
|
||||
require 'middleman-core/dependencies/vertices/file_vertex'
|
||||
|
||||
module Middleman
|
||||
class InlineURLRewriter < Filter
|
||||
|
@ -28,13 +31,13 @@ module Middleman
|
|||
@app.sitemap.by_destination_path(full_asset_path) || @app.sitemap.by_path(full_asset_path)
|
||||
end
|
||||
|
||||
Contract String => [String, Maybe[SetOf[String]]]
|
||||
Contract String => [String, ImmutableSetOf[::Middleman::Dependencies::Vertex]]
|
||||
def execute_filter(body)
|
||||
path = "/#{@resource.destination_path}"
|
||||
dirpath = ::Pathname.new(File.dirname(path))
|
||||
|
||||
::Middleman::Util.instrument 'inline_url_filter', path: path do
|
||||
deps = Set.new
|
||||
vertices = ::Hamster::Set.empty
|
||||
|
||||
new_content = ::Middleman::Util.rewrite_paths(body, path, @options.fetch(:url_extensions), @app) do |asset_path|
|
||||
uri = ::Middleman::Util.parse_uri(asset_path)
|
||||
|
@ -55,7 +58,7 @@ module Middleman
|
|||
|
||||
if result
|
||||
if @options.fetch(:create_dependencies, false)
|
||||
deps << ::Middleman::Dependencies::FileDependency.from_resource(
|
||||
vertices <<= ::Middleman::Dependencies::FileVertex.from_resource(
|
||||
target_resource(asset_path, dirpath)
|
||||
)
|
||||
end
|
||||
|
@ -66,7 +69,7 @@ module Middleman
|
|||
asset_path
|
||||
end
|
||||
|
||||
[new_content, deps]
|
||||
[new_content, vertices]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
require 'set'
|
||||
require 'hamster'
|
||||
require 'sass'
|
||||
require 'middleman-core/dependencies'
|
||||
require 'middleman-core/dependencies/vertices/file_vertex'
|
||||
|
||||
begin
|
||||
require 'sassc'
|
||||
|
@ -76,12 +77,10 @@ module Middleman
|
|||
end
|
||||
end
|
||||
|
||||
def dependencies
|
||||
files = @engine.dependencies.map do |d|
|
||||
::Middleman::Dependencies::FileDependency.new(@context.app.root_path, d.filename)
|
||||
def vertices
|
||||
@engine.dependencies.reduce(::Hamster::Set.empty) do |sum, d|
|
||||
sum << ::Middleman::Dependencies::FileVertex.new(@context.app.root_path, d.filename)
|
||||
end
|
||||
|
||||
files.empty? ? nil : Set.new(files)
|
||||
end
|
||||
|
||||
# Change Sass path, for url functions, to the build folder if we're building
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
require 'rack/mime'
|
||||
require 'set'
|
||||
require 'hamster'
|
||||
require 'middleman-core/sitemap/extensions/traversal'
|
||||
require 'middleman-core/file_renderer'
|
||||
require 'middleman-core/template_renderer'
|
||||
require 'middleman-core/contracts'
|
||||
require 'set'
|
||||
require 'middleman-core/inline_url_filter'
|
||||
require 'middleman-core/dependencies/vertices/vertex'
|
||||
|
||||
module Middleman
|
||||
# Sitemap namespace
|
||||
|
@ -39,8 +41,8 @@ module Middleman
|
|||
Contract Num
|
||||
attr_reader :priority
|
||||
|
||||
Contract Maybe[SetOf[IsA['::Middleman::Dependencies::BaseDependency']]]
|
||||
attr_reader :dependencies
|
||||
Contract ImmutableSetOf[::Middleman::Dependencies::Vertex]
|
||||
attr_reader :vertices
|
||||
|
||||
# Initialize resource with parent store and URL
|
||||
# @param [Middleman::Sitemap::Store] store
|
||||
|
@ -54,7 +56,7 @@ module Middleman
|
|||
@ignored = false
|
||||
@filters = ::Hamster::SortedSet.empty
|
||||
@priority = priority
|
||||
@dependencies = Set.new
|
||||
@vertices = ::Hamster::Set.empty
|
||||
|
||||
source = Pathname(source) if source&.is_a?(String)
|
||||
|
||||
|
@ -175,7 +177,7 @@ module Middleman
|
|||
# @return [String]
|
||||
Contract Hash, Hash, Maybe[Proc] => String
|
||||
def render(options_hash = ::Middleman::EMPTY_HASH, locs = ::Middleman::EMPTY_HASH, &_block)
|
||||
@dependencies = Set.new
|
||||
@vertices = ::Hamster::Set.empty
|
||||
|
||||
body = render_without_filters(options_hash, locs)
|
||||
|
||||
|
@ -186,7 +188,7 @@ module Middleman
|
|||
output
|
||||
elsif filter.is_a?(Filter)
|
||||
result = filter.execute_filter(output)
|
||||
@dependencies |= result[1] unless result[1].nil?
|
||||
@vertices |= result[1]
|
||||
result[0]
|
||||
else
|
||||
output
|
||||
|
@ -219,7 +221,7 @@ module Middleman
|
|||
|
||||
renderer = ::Middleman::TemplateRenderer.new(@app, file_descriptor[:full_path].to_s)
|
||||
renderer.render(locs, opts).to_str.tap do
|
||||
@dependencies |= renderer.dependencies unless renderer.dependencies.nil?
|
||||
@vertices |= renderer.vertices
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
require 'pathname'
|
||||
require 'hamster'
|
||||
require 'middleman-core/file_renderer'
|
||||
require 'middleman-core/template_renderer'
|
||||
require 'middleman-core/contracts'
|
||||
require 'middleman-core/dependencies/vertices/vertex'
|
||||
require 'middleman-core/dependencies/vertices/file_vertex'
|
||||
|
||||
module Middleman
|
||||
# The TemplateContext Class
|
||||
|
@ -22,8 +25,8 @@ module Middleman
|
|||
# Required for Padrino's rendering
|
||||
attr_accessor :current_engine
|
||||
|
||||
Contract Maybe[SetOf[IsA['::Middleman::Dependencies::BaseDependency']]]
|
||||
attr_reader :dependencies
|
||||
Contract ImmutableSetOf[::Middleman::Dependencies::Vertex]
|
||||
attr_reader :vertices
|
||||
|
||||
# Shorthand references to global values on the app instance.
|
||||
def_delegators :@app, :config, :logger, :sitemap, :server?, :build?, :environment?, :environment, :data, :extensions, :root, :development?, :production?
|
||||
|
@ -38,7 +41,7 @@ module Middleman
|
|||
@locs = locs
|
||||
@opts = options_hash
|
||||
|
||||
@dependencies = Set.new
|
||||
@vertices = ::Hamster::Set.empty
|
||||
end
|
||||
|
||||
# Return the current buffer to the caller and clear the value internally.
|
||||
|
@ -91,7 +94,7 @@ module Middleman
|
|||
restore_buffer(buf_was)
|
||||
end
|
||||
|
||||
@dependencies << ::Middleman::Dependencies::FileDependency.from_source_file(@app, layout_file)
|
||||
@vertices <<= ::Middleman::Dependencies::FileVertex.from_source_file(@app, layout_file)
|
||||
|
||||
# Render the layout, with the contents of the block inside.
|
||||
concat_safe_content render_file(layout_file, @locs, @opts) { content }
|
||||
|
@ -118,7 +121,7 @@ module Middleman
|
|||
source_path = sitemap.file_to_path(partial_file)
|
||||
r = sitemap.by_path(source_path)
|
||||
|
||||
@dependencies << ::Middleman::Dependencies::FileDependency.from_source_file(@app, partial_file)
|
||||
@vertices <<= ::Middleman::Dependencies::FileVertex.from_source_file(@app, partial_file)
|
||||
|
||||
result = if (r && !r.template?) || (Tilt[partial_file[:full_path]].nil? && partial_file[:full_path].exist?)
|
||||
partial_file.read
|
||||
|
@ -218,7 +221,7 @@ module Middleman
|
|||
|
||||
content_renderer = ::Middleman::FileRenderer.new(@app, path)
|
||||
content = content_renderer.render(locs, opts, context, &block)
|
||||
@dependencies |= content_renderer.dependencies unless content_renderer.dependencies.nil?
|
||||
@vertices |= content_renderer.vertices
|
||||
|
||||
path = File.basename(path, File.extname(path))
|
||||
rescue LocalJumpError
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
require 'tilt'
|
||||
require 'set'
|
||||
require 'hamster'
|
||||
require 'active_support/core_ext/string/output_safety'
|
||||
require 'middleman-core/contracts'
|
||||
require 'middleman-core/template_context'
|
||||
require 'middleman-core/file_renderer'
|
||||
require 'middleman-core/dependencies'
|
||||
require 'middleman-core/contracts'
|
||||
require 'middleman-core/dependencies/vertices/vertex'
|
||||
require 'middleman-core/dependencies/vertices/file_vertex'
|
||||
|
||||
module Middleman
|
||||
class TemplateRenderer
|
||||
|
@ -100,13 +101,13 @@ module Middleman
|
|||
# Custom error class for handling
|
||||
class TemplateNotFound < RuntimeError; end
|
||||
|
||||
Contract Maybe[SetOf[IsA['::Middleman::Dependencies::BaseDependency']]]
|
||||
attr_reader :dependencies
|
||||
Contract ImmutableSetOf[::Middleman::Dependencies::Vertex]
|
||||
attr_reader :vertices
|
||||
|
||||
def initialize(app, path)
|
||||
@app = app
|
||||
@path = path
|
||||
@dependencies = nil
|
||||
@vertices = ::Hamster::Set.empty
|
||||
end
|
||||
|
||||
# Render a template, with layout, given a path
|
||||
|
@ -174,8 +175,8 @@ module Middleman
|
|||
|
||||
::Middleman::Util.instrument 'builder.output.resource.render-layout', path: File.basename(layout_file[:relative_path].to_s) do
|
||||
layout_renderer.render(locals, options, context) { content }.tap do
|
||||
@dependencies << ::Middleman::Dependencies::FileDependency.from_source_file(@app, layout_file)
|
||||
@dependencies |= layout_renderer.dependencies unless layout_renderer.dependencies.nil?
|
||||
@vertices <<= ::Middleman::Dependencies::FileVertex.from_source_file(@app, layout_file)
|
||||
@vertices |= layout_renderer.vertices
|
||||
end
|
||||
end
|
||||
else
|
||||
|
@ -183,8 +184,7 @@ module Middleman
|
|||
end
|
||||
end
|
||||
|
||||
@dependencies |= context.dependencies unless context.dependencies.nil?
|
||||
@dependencies = @dependencies.empty? ? nil : @dependencies
|
||||
@vertices |= context.vertices
|
||||
|
||||
# Return result
|
||||
content
|
||||
|
@ -201,7 +201,7 @@ module Middleman
|
|||
# handles cases like `style.css.sass.erb`
|
||||
content = nil
|
||||
|
||||
@dependencies = Set.new
|
||||
@vertices = ::Hamster::Set.empty
|
||||
|
||||
while ::Middleman::Util.tilt_class(path)
|
||||
begin
|
||||
|
@ -209,7 +209,7 @@ module Middleman
|
|||
|
||||
content_renderer = ::Middleman::FileRenderer.new(@app, path)
|
||||
content = content_renderer.render(locs, opts, context, &block)
|
||||
@dependencies |= content_renderer.dependencies unless content_renderer.dependencies.nil?
|
||||
@vertices |= content_renderer.vertices
|
||||
|
||||
path = path.sub(/\.[^.]*\z/, '')
|
||||
rescue LocalJumpError
|
||||
|
|
Loading…
Add table
Reference in a new issue