middleman-core 4.1.0.rc.2 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/features/asset_hash.feature +30 -32
- data/features/asset_host.feature +2 -0
- data/features/gzip.feature +1 -1
- data/features/import_files.feature +0 -2
- data/features/nested_layouts.feature +20 -17
- data/fixtures/asset-host-app/source/javascripts/asset_host.js +2 -0
- data/fixtures/frontmatter-neighbor-app/config.rb +1 -1
- data/fixtures/frontmatter-settings-neighbor-app/config.rb +1 -1
- data/fixtures/nested-layout-app/source/layouts/inner.erb +5 -2
- data/fixtures/nested-layout-app/source/layouts/inner_haml.haml +6 -2
- data/fixtures/nested-layout-app/source/layouts/inner_slim.slim +6 -2
- data/fixtures/nested-layout-app/source/layouts/master.erb +7 -1
- data/fixtures/nested-layout-app/source/layouts/master_haml.haml +5 -1
- data/fixtures/nested-layout-app/source/layouts/master_slim.slim +5 -1
- data/fixtures/nested-layout-app/source/layouts/outer.erb +6 -2
- data/fixtures/nested-layout-app/source/layouts/outer_haml.haml +5 -1
- data/fixtures/nested-layout-app/source/layouts/outer_slim.slim +5 -1
- data/lib/middleman-core.rb +0 -3
- data/lib/middleman-core/application.rb +7 -9
- data/lib/middleman-core/builder.rb +88 -44
- data/lib/middleman-core/contracts.rb +102 -13
- data/lib/middleman-core/core_extensions/data.rb +15 -10
- data/lib/middleman-core/core_extensions/default_helpers.rb +15 -6
- data/lib/middleman-core/core_extensions/file_watcher.rb +2 -2
- data/lib/middleman-core/core_extensions/front_matter.rb +11 -3
- data/lib/middleman-core/core_extensions/i18n.rb +1 -1
- data/lib/middleman-core/core_extensions/inline_url_rewriter.rb +2 -2
- data/lib/middleman-core/extension.rb +1 -1
- data/lib/middleman-core/extensions.rb +1 -1
- data/lib/middleman-core/extensions/asset_hash.rb +1 -1
- data/lib/middleman-core/extensions/asset_host.rb +1 -1
- data/lib/middleman-core/extensions/automatic_image_sizes.rb +1 -1
- data/lib/middleman-core/extensions/cache_buster.rb +1 -1
- data/lib/middleman-core/extensions/external_pipeline.rb +2 -1
- data/lib/middleman-core/extensions/gzip.rb +2 -2
- data/lib/middleman-core/extensions/minify_css.rb +1 -1
- data/lib/middleman-core/extensions/minify_javascript.rb +1 -1
- data/lib/middleman-core/extensions/relative_assets.rb +1 -1
- data/lib/middleman-core/file_renderer.rb +12 -9
- data/lib/middleman-core/logger.rb +1 -0
- data/lib/middleman-core/preview_server.rb +14 -14
- data/lib/middleman-core/renderers/haml.rb +3 -1
- data/lib/middleman-core/renderers/less.rb +1 -1
- data/lib/middleman-core/renderers/liquid.rb +1 -1
- data/lib/middleman-core/renderers/sass.rb +7 -2
- data/lib/middleman-core/sitemap/extensions/ignores.rb +2 -2
- data/lib/middleman-core/sitemap/extensions/import.rb +3 -1
- data/lib/middleman-core/sitemap/resource.rb +7 -6
- data/lib/middleman-core/sources.rb +30 -13
- data/lib/middleman-core/sources/source_watcher.rb +50 -12
- data/lib/middleman-core/step_definitions/middleman_steps.rb +2 -2
- data/lib/middleman-core/template_context.rb +1 -1
- data/lib/middleman-core/template_renderer.rb +13 -4
- data/lib/middleman-core/util.rb +6 -606
- data/lib/middleman-core/util/binary.rb +79 -0
- data/lib/middleman-core/util/data.rb +37 -8
- data/lib/middleman-core/util/files.rb +134 -0
- data/lib/middleman-core/util/paths.rb +251 -0
- data/lib/middleman-core/util/rack.rb +52 -0
- data/lib/middleman-core/util/uri_templates.rb +97 -0
- data/lib/middleman-core/version.rb +1 -1
- data/middleman-core.gemspec +1 -0
- metadata +25 -4
@@ -0,0 +1,79 @@
|
|
1
|
+
# Template and Mime detection
|
2
|
+
require 'tilt'
|
3
|
+
require 'rack/mime'
|
4
|
+
|
5
|
+
require 'middleman-core/contracts'
|
6
|
+
|
7
|
+
module Middleman
|
8
|
+
module Util
|
9
|
+
include Contracts
|
10
|
+
|
11
|
+
module_function
|
12
|
+
|
13
|
+
# Whether the source file is binary.
|
14
|
+
#
|
15
|
+
# @param [String] filename The file to check.
|
16
|
+
# @return [Boolean]
|
17
|
+
Contract Or[String, Pathname] => Bool
|
18
|
+
def binary?(filename)
|
19
|
+
@binary_cache ||= {}
|
20
|
+
|
21
|
+
return @binary_cache[filename] if @binary_cache.key?(filename)
|
22
|
+
|
23
|
+
@binary_cache[filename] = begin
|
24
|
+
path = Pathname(filename)
|
25
|
+
ext = path.extname
|
26
|
+
|
27
|
+
# We hardcode detecting of gzipped SVG files
|
28
|
+
if ext == '.svgz'
|
29
|
+
true
|
30
|
+
elsif ::Tilt.registered?(ext.sub('.', ''))
|
31
|
+
false
|
32
|
+
else
|
33
|
+
dot_ext = (ext.to_s[0] == '.') ? ext.dup : ".#{ext}"
|
34
|
+
|
35
|
+
if mime = ::Rack::Mime.mime_type(dot_ext, nil)
|
36
|
+
!nonbinary_mime?(mime)
|
37
|
+
else
|
38
|
+
file_contents_include_binary_bytes?(path.to_s)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Is mime type known to be non-binary?
|
45
|
+
#
|
46
|
+
# @param [String] mime The mimetype to check.
|
47
|
+
# @return [Boolean]
|
48
|
+
Contract String => Bool
|
49
|
+
def nonbinary_mime?(mime)
|
50
|
+
case
|
51
|
+
when mime.start_with?('text/')
|
52
|
+
true
|
53
|
+
when mime.include?('xml') && !mime.include?('officedocument')
|
54
|
+
true
|
55
|
+
when mime.include?('json')
|
56
|
+
true
|
57
|
+
when mime.include?('javascript')
|
58
|
+
true
|
59
|
+
else
|
60
|
+
false
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Read a few bytes from the file and see if they are binary.
|
65
|
+
#
|
66
|
+
# @param [String] filename The file to check.
|
67
|
+
# @return [Boolean]
|
68
|
+
Contract String => Bool
|
69
|
+
def file_contents_include_binary_bytes?(filename)
|
70
|
+
binary_bytes = [0, 1, 2, 3, 4, 5, 6, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 29, 30, 31]
|
71
|
+
s = ::File.read(filename, 4096) || ''
|
72
|
+
s.each_byte do |c|
|
73
|
+
return true if binary_bytes.include?(c)
|
74
|
+
end
|
75
|
+
|
76
|
+
false
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -1,12 +1,40 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'json'
|
3
3
|
require 'pathname'
|
4
|
-
require 'middleman-core/util'
|
5
|
-
require 'middleman-core/contracts'
|
6
4
|
require 'backports/2.1.0/array/to_h'
|
5
|
+
require 'hashie'
|
6
|
+
|
7
|
+
require 'middleman-core/util/binary'
|
8
|
+
require 'middleman-core/contracts'
|
7
9
|
|
8
10
|
module Middleman
|
9
11
|
module Util
|
12
|
+
include Contracts
|
13
|
+
|
14
|
+
module_function
|
15
|
+
|
16
|
+
class EnhancedHash < ::Hashie::Mash
|
17
|
+
# include ::Hashie::Extensions::MergeInitializer
|
18
|
+
# include ::Hashie::Extensions::MethodReader
|
19
|
+
# include ::Hashie::Extensions::IndifferentAccess
|
20
|
+
end
|
21
|
+
|
22
|
+
# Recursively convert a normal Hash into a EnhancedHash
|
23
|
+
#
|
24
|
+
# @private
|
25
|
+
# @param [Hash] data Normal hash
|
26
|
+
# @return [Hash]
|
27
|
+
Contract Any => Maybe[Or[Array, EnhancedHash]]
|
28
|
+
def recursively_enhance(obj)
|
29
|
+
if obj.is_a? ::Array
|
30
|
+
obj.map { |e| recursively_enhance(e) }
|
31
|
+
elsif obj.is_a? ::Hash
|
32
|
+
::Hashie::Mash.new(obj)
|
33
|
+
else
|
34
|
+
obj
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
10
38
|
module Data
|
11
39
|
include Contracts
|
12
40
|
|
@@ -15,14 +43,15 @@ module Middleman
|
|
15
43
|
# Get the frontmatter and plain content from a file
|
16
44
|
# @param [String] path
|
17
45
|
# @return [Array<Hash, String>]
|
18
|
-
Contract
|
19
|
-
def parse(
|
20
|
-
|
46
|
+
Contract IsA['Middleman::SourceFile'], Maybe[Symbol] => [Hash, Maybe[String]]
|
47
|
+
def parse(file, frontmatter_delims, known_type=nil)
|
48
|
+
full_path = file[:full_path]
|
49
|
+
return [{}, nil] if ::Middleman::Util.binary?(full_path) || file[:types].include?(:binary)
|
21
50
|
|
22
51
|
# Avoid weird race condition when a file is renamed
|
23
52
|
begin
|
24
|
-
content =
|
25
|
-
rescue EOFError, IOError, Errno::ENOENT
|
53
|
+
content = file.read
|
54
|
+
rescue EOFError, IOError, ::Errno::ENOENT
|
26
55
|
return [{}, nil]
|
27
56
|
end
|
28
57
|
|
@@ -30,7 +59,7 @@ module Middleman
|
|
30
59
|
.values
|
31
60
|
.flatten(1)
|
32
61
|
.transpose
|
33
|
-
.map(
|
62
|
+
.map(&::Regexp.method(:union))
|
34
63
|
|
35
64
|
match = /
|
36
65
|
\A(?:[^\r\n]*coding:[^\r\n]*\r?\n)?
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module Middleman
|
2
|
+
module Util
|
3
|
+
include Contracts
|
4
|
+
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# Get a recusive list of files inside a path.
|
8
|
+
# Works with symlinks.
|
9
|
+
#
|
10
|
+
# @param path Some path string or Pathname
|
11
|
+
# @param ignore A proc/block that returns true if a given path should be ignored - if a path
|
12
|
+
# is ignored, nothing below it will be searched either.
|
13
|
+
# @return [Array<Pathname>] An array of Pathnames for each file (no directories)
|
14
|
+
Contract Or[String, Pathname], Proc => ArrayOf[Pathname]
|
15
|
+
def all_files_under(path, &ignore)
|
16
|
+
path = Pathname(path)
|
17
|
+
|
18
|
+
if ignore && yield(path)
|
19
|
+
[]
|
20
|
+
elsif path.directory?
|
21
|
+
path.children.flat_map do |child|
|
22
|
+
all_files_under(child, &ignore)
|
23
|
+
end.compact
|
24
|
+
elsif path.file?
|
25
|
+
[path]
|
26
|
+
else
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Glob a directory and try to keep path encoding consistent.
|
32
|
+
#
|
33
|
+
# @param [String] path The glob path.
|
34
|
+
# @return [Array<String>]
|
35
|
+
def glob_directory(path)
|
36
|
+
results = ::Dir[path]
|
37
|
+
|
38
|
+
return results unless RUBY_PLATFORM =~ /darwin/
|
39
|
+
|
40
|
+
results.map { |r| r.encode('UTF-8', 'UTF-8-MAC') }
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get the PWD and try to keep path encoding consistent.
|
44
|
+
#
|
45
|
+
# @param [String] path The glob path.
|
46
|
+
# @return [Array<String>]
|
47
|
+
def current_directory
|
48
|
+
result = ::Dir.pwd
|
49
|
+
|
50
|
+
return result unless RUBY_PLATFORM =~ /darwin/
|
51
|
+
|
52
|
+
result.encode('UTF-8', 'UTF-8-MAC')
|
53
|
+
end
|
54
|
+
|
55
|
+
Contract String => String
|
56
|
+
def step_through_extensions(path)
|
57
|
+
while ::Tilt[path]
|
58
|
+
ext = ::File.extname(path)
|
59
|
+
yield ext if block_given?
|
60
|
+
|
61
|
+
# Strip templating extensions as long as Tilt knows them
|
62
|
+
path = path[0..-(ext.length + 1)]
|
63
|
+
end
|
64
|
+
|
65
|
+
yield ::File.extname(path) if block_given?
|
66
|
+
|
67
|
+
path
|
68
|
+
end
|
69
|
+
|
70
|
+
# Removes the templating extensions, while keeping the others
|
71
|
+
# @param [String] path
|
72
|
+
# @return [String]
|
73
|
+
Contract String => String
|
74
|
+
def remove_templating_extensions(path)
|
75
|
+
step_through_extensions(path)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Removes the templating extensions, while keeping the others
|
79
|
+
# @param [String] path
|
80
|
+
# @return [String]
|
81
|
+
Contract String => ArrayOf[String]
|
82
|
+
def collect_extensions(path)
|
83
|
+
return [] if ::File.basename(path).start_with?('.')
|
84
|
+
|
85
|
+
result = []
|
86
|
+
|
87
|
+
step_through_extensions(path) { |e| result << e }
|
88
|
+
|
89
|
+
result
|
90
|
+
end
|
91
|
+
|
92
|
+
# Finds files which should also be considered to be dirty when
|
93
|
+
# the given file(s) are touched.
|
94
|
+
#
|
95
|
+
# @param [Middleman::Application] app The app.
|
96
|
+
# @param [Pathname] files The original touched file paths.
|
97
|
+
# @return [Middleman::SourceFile] All related file paths, not including the source file paths.
|
98
|
+
Contract ::Middleman::Application, ArrayOf[Pathname] => ArrayOf[::Middleman::SourceFile]
|
99
|
+
def find_related_files(app, files)
|
100
|
+
return [] if files.empty?
|
101
|
+
|
102
|
+
all_extensions = files.flat_map { |f| collect_extensions(f.to_s) }
|
103
|
+
|
104
|
+
sass_type_aliasing = ['.scss', '.sass']
|
105
|
+
erb_type_aliasing = ['.erb', '.haml', '.slim']
|
106
|
+
|
107
|
+
if (all_extensions & sass_type_aliasing).length > 0
|
108
|
+
all_extensions |= sass_type_aliasing
|
109
|
+
end
|
110
|
+
|
111
|
+
if (all_extensions & erb_type_aliasing).length > 0
|
112
|
+
all_extensions |= erb_type_aliasing
|
113
|
+
end
|
114
|
+
|
115
|
+
all_extensions.uniq!
|
116
|
+
|
117
|
+
app.sitemap.resources.select(&:file_descriptor).select { |r|
|
118
|
+
local_extensions = collect_extensions(r.file_descriptor[:full_path].to_s)
|
119
|
+
|
120
|
+
if (local_extensions & sass_type_aliasing).length > 0
|
121
|
+
local_extensions |= sass_type_aliasing
|
122
|
+
end
|
123
|
+
|
124
|
+
if (local_extensions & erb_type_aliasing).length > 0
|
125
|
+
local_extensions |= erb_type_aliasing
|
126
|
+
end
|
127
|
+
|
128
|
+
local_extensions.uniq!
|
129
|
+
|
130
|
+
((all_extensions & local_extensions).length > 0) && files.none? { |f| f == r.file_descriptor[:full_path] }
|
131
|
+
}.map(&:file_descriptor)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
# Core Pathname library used for traversal
|
2
|
+
require 'pathname'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
require 'middleman-core/contracts'
|
6
|
+
|
7
|
+
# rubocop:disable ModuleLength
|
8
|
+
module Middleman
|
9
|
+
module Util
|
10
|
+
include Contracts
|
11
|
+
|
12
|
+
module_function
|
13
|
+
|
14
|
+
# Normalize a path to not include a leading slash
|
15
|
+
# @param [String] path
|
16
|
+
# @return [String]
|
17
|
+
Contract String => String
|
18
|
+
def normalize_path(path)
|
19
|
+
# The tr call works around a bug in Ruby's Unicode handling
|
20
|
+
::URI.decode(path).sub(%r{^/}, '').tr('', '')
|
21
|
+
end
|
22
|
+
|
23
|
+
# This is a separate method from normalize_path in case we
|
24
|
+
# change how we normalize paths
|
25
|
+
Contract String => String
|
26
|
+
def strip_leading_slash(path)
|
27
|
+
path.sub(%r{^/}, '')
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get the path of a file of a given type
|
31
|
+
#
|
32
|
+
# @param [Middleman::Application] app The app.
|
33
|
+
# @param [Symbol] kind The type of file
|
34
|
+
# @param [String, Symbol] source The path to the file
|
35
|
+
# @param [Hash] options Data to pass through.
|
36
|
+
# @return [String]
|
37
|
+
Contract ::Middleman::Application, Symbol, Or[String, Symbol], Hash => String
|
38
|
+
def asset_path(app, kind, source, options={})
|
39
|
+
return source if source.to_s.include?('//') || source.to_s.start_with?('data:')
|
40
|
+
|
41
|
+
asset_folder = case kind
|
42
|
+
when :css
|
43
|
+
app.config[:css_dir]
|
44
|
+
when :js
|
45
|
+
app.config[:js_dir]
|
46
|
+
when :images
|
47
|
+
app.config[:images_dir]
|
48
|
+
when :fonts
|
49
|
+
app.config[:fonts_dir]
|
50
|
+
else
|
51
|
+
kind.to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
source = source.to_s.tr(' ', '')
|
55
|
+
ignore_extension = (kind == :images || kind == :fonts) # don't append extension
|
56
|
+
source << ".#{kind}" unless ignore_extension || source.end_with?(".#{kind}")
|
57
|
+
asset_folder = '' if source.start_with?('/') # absolute path
|
58
|
+
|
59
|
+
asset_url(app, source, asset_folder, options)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Get the URL of an asset given a type/prefix
|
63
|
+
#
|
64
|
+
# @param [String] path The path (such as "photo.jpg")
|
65
|
+
# @param [String] prefix The type prefix (such as "images")
|
66
|
+
# @param [Hash] options Data to pass through.
|
67
|
+
# @return [String] The fully qualified asset url
|
68
|
+
Contract ::Middleman::Application, String, String, Hash => String
|
69
|
+
def asset_url(app, path, prefix='', options={})
|
70
|
+
# Don't touch assets which already have a full path
|
71
|
+
return path if path.include?('//') || path.start_with?('data:')
|
72
|
+
|
73
|
+
if options[:relative] && !options[:current_resource]
|
74
|
+
raise ArgumentError, '#asset_url must be run in a context with current_resource if relative: true'
|
75
|
+
end
|
76
|
+
|
77
|
+
uri = URI(path)
|
78
|
+
path = uri.path
|
79
|
+
|
80
|
+
result = if resource = app.sitemap.find_resource_by_destination_path(url_for(app, path, options))
|
81
|
+
resource.url
|
82
|
+
else
|
83
|
+
path = ::File.join(prefix, path)
|
84
|
+
if resource = app.sitemap.find_resource_by_path(path)
|
85
|
+
resource.url
|
86
|
+
else
|
87
|
+
::File.join(app.config[:http_prefix], path)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
final_result = ::URI.encode(relative_path_from_resource(options[:current_resource], result, options[:relative]))
|
92
|
+
|
93
|
+
result_uri = URI(final_result)
|
94
|
+
result_uri.query = uri.query
|
95
|
+
result_uri.fragment = uri.fragment
|
96
|
+
result_uri.to_s
|
97
|
+
end
|
98
|
+
|
99
|
+
# Given a source path (referenced either absolutely or relatively)
|
100
|
+
# or a Resource, this will produce the nice URL configured for that
|
101
|
+
# path, respecting :relative_links, directory indexes, etc.
|
102
|
+
Contract ::Middleman::Application, Or[String, ::Middleman::Sitemap::Resource], Hash => String
|
103
|
+
def url_for(app, path_or_resource, options={})
|
104
|
+
# Handle Resources and other things which define their own url method
|
105
|
+
url = if path_or_resource.respond_to?(:url)
|
106
|
+
path_or_resource.url
|
107
|
+
else
|
108
|
+
path_or_resource.dup
|
109
|
+
end
|
110
|
+
|
111
|
+
# Try to parse URL
|
112
|
+
begin
|
113
|
+
uri = URI(url)
|
114
|
+
rescue ::URI::InvalidURIError
|
115
|
+
# Nothing we can do with it, it's not really a URI
|
116
|
+
return url
|
117
|
+
end
|
118
|
+
|
119
|
+
relative = options[:relative]
|
120
|
+
raise "Can't use the relative option with an external URL" if relative && uri.host
|
121
|
+
|
122
|
+
# Allow people to turn on relative paths for all links with
|
123
|
+
# set :relative_links, true
|
124
|
+
# but still override on a case by case basis with the :relative parameter.
|
125
|
+
effective_relative = relative || false
|
126
|
+
effective_relative = true if relative.nil? && app.config[:relative_links]
|
127
|
+
|
128
|
+
# Try to find a sitemap resource corresponding to the desired path
|
129
|
+
this_resource = options[:current_resource]
|
130
|
+
|
131
|
+
if path_or_resource.is_a?(::Middleman::Sitemap::Resource)
|
132
|
+
resource = path_or_resource
|
133
|
+
resource_url = url
|
134
|
+
elsif this_resource && uri.path && !uri.host
|
135
|
+
# Handle relative urls
|
136
|
+
url_path = Pathname(uri.path)
|
137
|
+
current_source_dir = Pathname('/' + this_resource.path).dirname
|
138
|
+
url_path = current_source_dir.join(url_path) if url_path.relative?
|
139
|
+
resource = app.sitemap.find_resource_by_path(url_path.to_s)
|
140
|
+
if resource
|
141
|
+
resource_url = resource.url
|
142
|
+
else
|
143
|
+
# Try to find a resource relative to destination paths
|
144
|
+
url_path = Pathname(uri.path)
|
145
|
+
current_source_dir = Pathname('/' + this_resource.destination_path).dirname
|
146
|
+
url_path = current_source_dir.join(url_path) if url_path.relative?
|
147
|
+
resource = app.sitemap.find_resource_by_destination_path(url_path.to_s)
|
148
|
+
resource_url = resource.url if resource
|
149
|
+
end
|
150
|
+
elsif options[:find_resource] && uri.path && !uri.host
|
151
|
+
resource = app.sitemap.find_resource_by_path(uri.path)
|
152
|
+
resource_url = resource.url if resource
|
153
|
+
end
|
154
|
+
|
155
|
+
if resource
|
156
|
+
uri.path = if this_resource
|
157
|
+
::URI.encode(relative_path_from_resource(this_resource, resource_url, effective_relative))
|
158
|
+
else
|
159
|
+
resource_url
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Support a :query option that can be a string or hash
|
164
|
+
if query = options[:query]
|
165
|
+
uri.query = query.respond_to?(:to_param) ? query.to_param : query.to_s
|
166
|
+
end
|
167
|
+
|
168
|
+
# Support a :fragment or :anchor option just like Padrino
|
169
|
+
fragment = options[:anchor] || options[:fragment]
|
170
|
+
uri.fragment = fragment.to_s if fragment
|
171
|
+
|
172
|
+
# Finally make the URL back into a string
|
173
|
+
uri.to_s
|
174
|
+
end
|
175
|
+
|
176
|
+
# Expand a path to include the index file if it's a directory
|
177
|
+
#
|
178
|
+
# @param [String] path Request path/
|
179
|
+
# @param [Middleman::Application] app The requesting app.
|
180
|
+
# @return [String] Path with index file if necessary.
|
181
|
+
Contract String, ::Middleman::Application => String
|
182
|
+
def full_path(path, app)
|
183
|
+
resource = app.sitemap.find_resource_by_destination_path(path)
|
184
|
+
|
185
|
+
unless resource
|
186
|
+
# Try it with /index.html at the end
|
187
|
+
indexed_path = ::File.join(path.sub(%r{/$}, ''), app.config[:index_file])
|
188
|
+
resource = app.sitemap.find_resource_by_destination_path(indexed_path)
|
189
|
+
end
|
190
|
+
|
191
|
+
if resource
|
192
|
+
'/' + resource.destination_path
|
193
|
+
else
|
194
|
+
'/' + normalize_path(path)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Get a relative path to a resource.
|
199
|
+
#
|
200
|
+
# @param [Middleman::Sitemap::Resource] curr_resource The resource.
|
201
|
+
# @param [String] resource_url The target url.
|
202
|
+
# @param [Boolean] relative If the path should be relative.
|
203
|
+
# @return [String]
|
204
|
+
Contract ::Middleman::Sitemap::Resource, String, Bool => String
|
205
|
+
def relative_path_from_resource(curr_resource, resource_url, relative)
|
206
|
+
# Switch to the relative path between resource and the given resource
|
207
|
+
# if we've been asked to.
|
208
|
+
if relative
|
209
|
+
# Output urls relative to the destination path, not the source path
|
210
|
+
current_dir = Pathname('/' + curr_resource.destination_path).dirname
|
211
|
+
relative_path = Pathname(resource_url).relative_path_from(current_dir).to_s
|
212
|
+
|
213
|
+
# Put back the trailing slash to avoid unnecessary Apache redirects
|
214
|
+
if resource_url.end_with?('/') && !relative_path.end_with?('/')
|
215
|
+
relative_path << '/'
|
216
|
+
end
|
217
|
+
|
218
|
+
relative_path
|
219
|
+
else
|
220
|
+
resource_url
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Takes a matcher, which can be a literal string
|
225
|
+
# or a string containing glob expressions, or a
|
226
|
+
# regexp, or a proc, or anything else that responds
|
227
|
+
# to #match or #call, and returns whether or not the
|
228
|
+
# given path matches that matcher.
|
229
|
+
#
|
230
|
+
# @param [String, #match, #call] matcher A matcher String, RegExp, Proc, etc.
|
231
|
+
# @param [String] path A path as a string
|
232
|
+
# @return [Boolean] Whether the path matches the matcher
|
233
|
+
Contract PATH_MATCHER, String => Bool
|
234
|
+
def path_match(matcher, path)
|
235
|
+
case
|
236
|
+
when matcher.is_a?(String)
|
237
|
+
if matcher.include? '*'
|
238
|
+
::File.fnmatch(matcher, path)
|
239
|
+
else
|
240
|
+
path == matcher
|
241
|
+
end
|
242
|
+
when matcher.respond_to?(:match)
|
243
|
+
!!(path =~ matcher)
|
244
|
+
when matcher.respond_to?(:call)
|
245
|
+
matcher.call(path)
|
246
|
+
else
|
247
|
+
::File.fnmatch(matcher.to_s, path)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|