middleman-core 4.0.0.beta.2 → 4.0.0.rc.1
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/builder.feature +9 -1
- data/features/collections.feature +43 -2
- data/features/data.feature +7 -0
- data/features/extension_hooks.feature +13 -0
- data/features/front-matter-neighbor.feature +0 -11
- data/features/front-matter.feature +0 -22
- data/features/markdown_kramdown_in_slim.feature +42 -0
- data/fixtures/extension-hooks-app/config.rb +39 -0
- data/fixtures/extension-hooks-app/source/index.html.erb +9 -0
- data/fixtures/frontmatter-app/source/front-matter-line-2.html.erb +4 -1
- data/fixtures/frontmatter-neighbor-app/config.rb +3 -3
- data/fixtures/frontmatter-settings-neighbor-app/config.rb +4 -4
- data/fixtures/markdown-in-slim-app/config.rb +0 -0
- data/fixtures/markdown-in-slim-app/source/images/blank.gif +0 -0
- data/fixtures/markdown-in-slim-app/source/link_target.html.markdown +4 -0
- data/fixtures/more-traversal-app/source/layout.erb +1 -1
- data/fixtures/nested-data-app/data/examples/withcontent.yaml +11 -0
- data/fixtures/nested-data-app/source/extracontent.html.haml.erb +4 -0
- data/fixtures/traversal-app/source/layout.erb +1 -1
- data/lib/middleman-core/application.rb +7 -3
- data/lib/middleman-core/builder.rb +2 -1
- data/lib/middleman-core/configuration.rb +0 -1
- data/lib/middleman-core/contracts.rb +0 -10
- data/lib/middleman-core/core_extensions/collections.rb +30 -6
- data/lib/middleman-core/core_extensions/data.rb +4 -4
- data/lib/middleman-core/core_extensions/file_watcher.rb +1 -1
- data/lib/middleman-core/core_extensions/front_matter.rb +10 -104
- data/lib/middleman-core/core_extensions/show_exceptions.rb +1 -1
- data/lib/middleman-core/extension_manager.rb +2 -1
- data/lib/middleman-core/extensions/asset_hash.rb +2 -1
- data/lib/middleman-core/extensions/asset_host.rb +1 -1
- data/lib/middleman-core/extensions/external_pipeline.rb +4 -1
- 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/extensions.rb +1 -1
- data/lib/middleman-core/file_renderer.rb +6 -4
- data/lib/middleman-core/meta_pages/sitemap_resource.rb +1 -1
- data/lib/middleman-core/meta_pages/templates/config.html.erb +14 -14
- data/lib/middleman-core/meta_pages.rb +6 -1
- data/lib/middleman-core/preview_server.rb +21 -2
- data/lib/middleman-core/rack.rb +5 -3
- data/lib/middleman-core/renderers/haml.rb +22 -6
- data/lib/middleman-core/renderers/kramdown.rb +10 -3
- data/lib/middleman-core/renderers/liquid.rb +22 -3
- data/lib/middleman-core/renderers/redcarpet.rb +7 -5
- data/lib/middleman-core/renderers/sass.rb +1 -5
- data/lib/middleman-core/renderers/slim.rb +16 -12
- data/lib/middleman-core/sitemap/extensions/ignores.rb +2 -0
- data/lib/middleman-core/sitemap/extensions/on_disk.rb +2 -0
- data/lib/middleman-core/sitemap/extensions/proxies.rb +18 -3
- data/lib/middleman-core/sitemap/extensions/redirects.rb +2 -0
- data/lib/middleman-core/sitemap/extensions/request_endpoints.rb +2 -0
- data/lib/middleman-core/sitemap/resource.rb +24 -17
- data/lib/middleman-core/sitemap/store.rb +1 -0
- data/lib/middleman-core/sources/source_watcher.rb +26 -1
- data/lib/middleman-core/step_definitions/builder_steps.rb +8 -8
- data/lib/middleman-core/step_definitions/middleman_steps.rb +8 -2
- data/lib/middleman-core/step_definitions/server_steps.rb +17 -18
- data/lib/middleman-core/step_definitions.rb +0 -1
- data/lib/middleman-core/template_context.rb +2 -2
- data/lib/middleman-core/util/data.rb +153 -0
- data/lib/middleman-core/util.rb +14 -45
- data/lib/middleman-core/version.rb +1 -1
- data/middleman-core.gemspec +3 -2
- data/spec/middleman-core/util_spec.rb +3 -23
- metadata +37 -26
- data/fixtures/frontmatter-app/source/json-front-matter-2.php.erb +0 -7
- data/fixtures/frontmatter-app/source/json-front-matter-encoding.html.erb +0 -7
- data/fixtures/frontmatter-app/source/json-front-matter-line-2.html.erb +0 -7
- data/fixtures/frontmatter-app/source/json-front-matter.html.erb +0 -6
- data/fixtures/frontmatter-neighbor-app/source/json-front-matter-2.php.erb +0 -2
- data/fixtures/frontmatter-neighbor-app/source/json-front-matter-2.php.erb.frontmatter +0 -4
- data/fixtures/frontmatter-neighbor-app/source/json-front-matter.html.erb +0 -1
- data/fixtures/frontmatter-neighbor-app/source/json-front-matter.html.erb.frontmatter +0 -4
@@ -7,6 +7,8 @@ module Middleman
|
|
7
7
|
# Manages the list of proxy configurations and manipulates the sitemap
|
8
8
|
# to include new resources based on those configurations
|
9
9
|
class Proxies < Extension
|
10
|
+
self.resource_list_manipulator_priority = 0
|
11
|
+
|
10
12
|
# Expose `create_proxy` as `app.proxy`
|
11
13
|
expose_to_application proxy: :create_proxy
|
12
14
|
|
@@ -83,11 +85,20 @@ module Middleman
|
|
83
85
|
end
|
84
86
|
end
|
85
87
|
|
88
|
+
class Resource
|
89
|
+
def proxy_to(path)
|
90
|
+
throw "Resource#proxy_to has been removed. Use ProxyResource class instead."
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
86
94
|
class ProxyResource < ::Middleman::Sitemap::Resource
|
95
|
+
Contract String
|
96
|
+
attr_reader :target
|
97
|
+
|
87
98
|
# Initialize resource with parent store and URL
|
88
99
|
# @param [Middleman::Sitemap::Store] store
|
89
100
|
# @param [String] path
|
90
|
-
# @param [String]
|
101
|
+
# @param [String] target
|
91
102
|
def initialize(store, path, target)
|
92
103
|
super(store, path)
|
93
104
|
|
@@ -115,8 +126,12 @@ module Middleman
|
|
115
126
|
end
|
116
127
|
|
117
128
|
Contract IsA['Middleman::SourceFile']
|
118
|
-
def
|
119
|
-
target_resource.
|
129
|
+
def file_descriptor
|
130
|
+
target_resource.file_descriptor
|
131
|
+
end
|
132
|
+
|
133
|
+
def metadata
|
134
|
+
target_resource.metadata.deep_merge super
|
120
135
|
end
|
121
136
|
|
122
137
|
Contract Maybe[String]
|
@@ -7,6 +7,8 @@ module Middleman
|
|
7
7
|
# Manages the list of proxy configurations and manipulates the sitemap
|
8
8
|
# to include new resources based on those configurations
|
9
9
|
class Redirects < Extension
|
10
|
+
self.resource_list_manipulator_priority = 0
|
11
|
+
|
10
12
|
# Expose `create_redirect` to config as `redirect`
|
11
13
|
expose_to_config redirect: :create_redirect
|
12
14
|
|
@@ -24,7 +24,7 @@ module Middleman
|
|
24
24
|
# The on-disk source file for this resource, if there is one
|
25
25
|
# @return [String]
|
26
26
|
Contract Maybe[IsA['Middleman::SourceFile']]
|
27
|
-
attr_reader :
|
27
|
+
attr_reader :file_descriptor
|
28
28
|
|
29
29
|
# The path to use when requesting this resource. Normally it's
|
30
30
|
# the same as {#destination_path} but it can be overridden in subclasses.
|
@@ -41,21 +41,21 @@ module Middleman
|
|
41
41
|
# Initialize resource with parent store and URL
|
42
42
|
# @param [Middleman::Sitemap::Store] store
|
43
43
|
# @param [String] path
|
44
|
-
# @param [String]
|
44
|
+
# @param [String] source
|
45
45
|
Contract IsA['Middleman::Sitemap::Store'], String, Maybe[Or[IsA['Middleman::SourceFile'], String]] => Any
|
46
|
-
def initialize(store, path,
|
46
|
+
def initialize(store, path, source=nil)
|
47
47
|
@store = store
|
48
48
|
@app = @store.app
|
49
49
|
@path = path
|
50
50
|
|
51
|
-
if
|
52
|
-
|
51
|
+
if source && source.is_a?(String)
|
52
|
+
source = Pathname(source)
|
53
53
|
end
|
54
54
|
|
55
|
-
if
|
56
|
-
@
|
55
|
+
if source && source.is_a?(Pathname)
|
56
|
+
@file_descriptor = ::Middleman::SourceFile.new(source.relative_path_from(@app.source_dir), source, @app.source_dir, Set.new([:source]))
|
57
57
|
else
|
58
|
-
@
|
58
|
+
@file_descriptor = source
|
59
59
|
end
|
60
60
|
|
61
61
|
@destination_path = @path
|
@@ -71,8 +71,15 @@ module Middleman
|
|
71
71
|
# @return [Boolean]
|
72
72
|
Contract Bool
|
73
73
|
def template?
|
74
|
-
return false if
|
75
|
-
!::Tilt[
|
74
|
+
return false if file_descriptor.nil?
|
75
|
+
!::Tilt[file_descriptor[:full_path].to_s].nil?
|
76
|
+
end
|
77
|
+
|
78
|
+
# Backwards compatible method for turning descriptor into a string.
|
79
|
+
# @return [String]
|
80
|
+
Contract String
|
81
|
+
def source_file
|
82
|
+
file_descriptor && file_descriptor[:full_path].to_s
|
76
83
|
end
|
77
84
|
|
78
85
|
# Merge in new metadata specific to this resource.
|
@@ -87,8 +94,8 @@ module Middleman
|
|
87
94
|
end
|
88
95
|
|
89
96
|
# Data about this resource, populated from frontmatter or extensions.
|
90
|
-
# @return [
|
91
|
-
Contract
|
97
|
+
# @return [Hash]
|
98
|
+
Contract RespondTo[:indifferent_access?]
|
92
99
|
def data
|
93
100
|
::Middleman::Util.recursively_enhance(metadata[:page])
|
94
101
|
end
|
@@ -119,9 +126,9 @@ module Middleman
|
|
119
126
|
# @return [String]
|
120
127
|
Contract Hash, Hash => String
|
121
128
|
def render(opts={}, locs={})
|
122
|
-
return ::Middleman::FileRenderer.new(@app,
|
129
|
+
return ::Middleman::FileRenderer.new(@app, file_descriptor[:full_path].to_s).template_data_for_file unless template?
|
123
130
|
|
124
|
-
::Middleman::Util.instrument 'render.resource', path:
|
131
|
+
::Middleman::Util.instrument 'render.resource', path: file_descriptor[:full_path].to_s, destination_path: destination_path do
|
125
132
|
md = metadata
|
126
133
|
opts = md[:options].deep_merge(opts)
|
127
134
|
locs = md[:locals].deep_merge(locs)
|
@@ -132,7 +139,7 @@ module Middleman
|
|
132
139
|
opts[:layout] = false if %w(.js .json .css .txt).include?(ext)
|
133
140
|
end
|
134
141
|
|
135
|
-
renderer = ::Middleman::TemplateRenderer.new(@app,
|
142
|
+
renderer = ::Middleman::TemplateRenderer.new(@app, file_descriptor[:full_path].to_s)
|
136
143
|
renderer.render(locs, opts)
|
137
144
|
end
|
138
145
|
end
|
@@ -155,7 +162,7 @@ module Middleman
|
|
155
162
|
# @return [Boolean]
|
156
163
|
Contract Bool
|
157
164
|
def binary?
|
158
|
-
!
|
165
|
+
!file_descriptor.nil? && ::Middleman::Util.binary?(file_descriptor[:full_path].to_s)
|
159
166
|
end
|
160
167
|
|
161
168
|
# Ignore a resource directly, without going through the whole
|
@@ -174,7 +181,7 @@ module Middleman
|
|
174
181
|
# Ignore based on the source path (without template extensions)
|
175
182
|
return true if @app.sitemap.ignored?(path)
|
176
183
|
# This allows files to be ignored by their source file name (with template extensions)
|
177
|
-
if !self.is_a?(ProxyResource) &&
|
184
|
+
if !self.is_a?(ProxyResource) && file_descriptor && @app.sitemap.ignored?(file_descriptor[:relative_path].to_s)
|
178
185
|
true
|
179
186
|
else
|
180
187
|
false
|
@@ -188,6 +188,7 @@ module Middleman
|
|
188
188
|
@app.logger.debug '== Rebuilding resource list'
|
189
189
|
|
190
190
|
@resources = @resource_list_manipulators.reduce([]) do |result, m|
|
191
|
+
@app.logger.debug "== Running manipulator: #{m[:name]}"
|
191
192
|
newres = m[:manipulator].send(m[:custom_name] || :manipulate_resource_list, result)
|
192
193
|
|
193
194
|
# Reset lookup cache
|
@@ -4,6 +4,26 @@ require 'middleman-core/contracts'
|
|
4
4
|
require 'middleman-core/contracts'
|
5
5
|
require 'backports/2.0.0/enumerable/lazy'
|
6
6
|
|
7
|
+
# Monkey patch Listen silencer so `only` works on directories too
|
8
|
+
module Listen
|
9
|
+
class Silencer
|
10
|
+
# TODO: switch type and path places - and verify
|
11
|
+
def silenced?(relative_path, type)
|
12
|
+
path = relative_path.to_s
|
13
|
+
|
14
|
+
# if only_patterns && type == :file
|
15
|
+
# return true unless only_patterns.any? { |pattern| path =~ pattern }
|
16
|
+
# end
|
17
|
+
|
18
|
+
if only_patterns
|
19
|
+
return !only_patterns.any? { |pattern| path =~ pattern }
|
20
|
+
end
|
21
|
+
|
22
|
+
ignore_patterns.any? { |pattern| path =~ pattern }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
7
27
|
module Middleman
|
8
28
|
# The default source watcher implementation. Watches a directory on disk
|
9
29
|
# and responds to events on changes.
|
@@ -29,6 +49,9 @@ module Middleman
|
|
29
49
|
Contract Hash
|
30
50
|
attr_reader :options
|
31
51
|
|
52
|
+
# Reference to lower level listener
|
53
|
+
attr_reader :listener
|
54
|
+
|
32
55
|
# Construct a new SourceWatcher
|
33
56
|
#
|
34
57
|
# @param [Middleman::Sources] parent The parent collection.
|
@@ -138,9 +161,11 @@ module Middleman
|
|
138
161
|
config[:latency] = @latency if @latency
|
139
162
|
|
140
163
|
@listener = ::Listen.to(@directory.to_s, config, &method(:on_listener_change))
|
141
|
-
@listener.start
|
142
164
|
|
165
|
+
@listener.ignore(/^\.sass-cache/)
|
143
166
|
@listener.only(@only) unless @only.empty?
|
167
|
+
|
168
|
+
@listener.start
|
144
169
|
end
|
145
170
|
|
146
171
|
# Stop the listener.
|
@@ -6,8 +6,8 @@ end
|
|
6
6
|
|
7
7
|
Given /^app "([^\"]*)" is using config "([^\"]*)"$/ do |path, config_name|
|
8
8
|
target = File.join(PROJECT_ROOT_PATH, 'fixtures', path)
|
9
|
-
config_path = File.join(
|
10
|
-
config_dest = File.join(
|
9
|
+
config_path = File.join(expand_path("."), "config-#{config_name}.rb")
|
10
|
+
config_dest = File.join(expand_path("."), 'config.rb')
|
11
11
|
FileUtils.cp(config_path, config_dest)
|
12
12
|
end
|
13
13
|
|
@@ -22,12 +22,12 @@ Given /^a fixture app "([^\"]*)"$/ do |path|
|
|
22
22
|
|
23
23
|
# This step can be reentered from several places but we don't want
|
24
24
|
# to keep re-copying and re-cd-ing into ever-deeper directories
|
25
|
-
next if File.basename(
|
25
|
+
next if File.basename(expand_path(".")) == path
|
26
26
|
|
27
27
|
step %Q{a directory named "#{path}"}
|
28
28
|
|
29
29
|
target_path = File.join(PROJECT_ROOT_PATH, 'fixtures', path)
|
30
|
-
FileUtils.cp_r(target_path,
|
30
|
+
FileUtils.cp_r(target_path, expand_path("."))
|
31
31
|
|
32
32
|
step %Q{I cd to "#{path}"}
|
33
33
|
end
|
@@ -58,20 +58,20 @@ Given /^a successfully built app at "([^\"]*)" with flags "([^\"]*)"$/ do |path,
|
|
58
58
|
end
|
59
59
|
|
60
60
|
Given /^a modification time for a file named "([^\"]*)"$/ do |file|
|
61
|
-
target = File.join(
|
61
|
+
target = File.join(expand_path("."), file)
|
62
62
|
@modification_times[target] = File.mtime(target)
|
63
63
|
end
|
64
64
|
|
65
65
|
Then /^the file "([^\"]*)" should not have been updated$/ do |file|
|
66
|
-
target = File.join(
|
66
|
+
target = File.join(expand_path("."), file)
|
67
67
|
expect(File.mtime(target)).to eq(@modification_times[target])
|
68
68
|
end
|
69
69
|
|
70
70
|
# Provide this Aruba overload in case we're matching something with quotes in it
|
71
71
|
Then /^the file "([^"]*)" should contain '([^']*)'$/ do |file, partial_content|
|
72
|
-
|
72
|
+
expect(file).to have_file_content(Regexp.new(Regexp.escape(partial_content)), true)
|
73
73
|
end
|
74
74
|
|
75
75
|
And /the file "(.*)" should be gzipped/ do |file|
|
76
|
-
expect(File.binread(File.join(
|
76
|
+
expect(File.binread(File.join(expand_path("."), file), 2)).to eq(['1F8B'].pack('H*'))
|
77
77
|
end
|
@@ -1,9 +1,15 @@
|
|
1
1
|
Then /^the file "([^\"]*)" has the contents$/ do |path, contents|
|
2
2
|
write_file(path, contents)
|
3
|
-
|
3
|
+
|
4
|
+
cd(".") do
|
5
|
+
@server_inst.files.find_new_files!
|
6
|
+
end
|
4
7
|
end
|
5
8
|
|
6
9
|
Then /^the file "([^\"]*)" is removed$/ do |path|
|
7
10
|
step %Q{I remove the file "#{path}"}
|
8
|
-
|
11
|
+
|
12
|
+
cd(".") do
|
13
|
+
@server_inst.files.find_new_files!
|
14
|
+
end
|
9
15
|
end
|
@@ -31,8 +31,7 @@ Given /^"([^\"]*)" is set to "([^\"]*)"$/ do |variable, value|
|
|
31
31
|
end
|
32
32
|
|
33
33
|
Given /^the Server is running$/ do
|
34
|
-
root_dir = File.expand_path(
|
35
|
-
|
34
|
+
root_dir = File.expand_path(expand_path("."))
|
36
35
|
|
37
36
|
if File.exists?(File.join(root_dir, 'source'))
|
38
37
|
ENV['MM_SOURCE'] = 'source'
|
@@ -44,7 +43,7 @@ Given /^the Server is running$/ do
|
|
44
43
|
|
45
44
|
initialize_commands = @initialize_commands || []
|
46
45
|
|
47
|
-
|
46
|
+
cd(".") do
|
48
47
|
@server_inst = ::Middleman::Application.new do
|
49
48
|
config[:watcher_disable] = true
|
50
49
|
config[:show_exceptions] = false
|
@@ -53,10 +52,10 @@ Given /^the Server is running$/ do
|
|
53
52
|
instance_exec(&p)
|
54
53
|
end
|
55
54
|
end
|
56
|
-
end
|
57
55
|
|
58
|
-
|
59
|
-
|
56
|
+
rack = ::Middleman::Rack.new(@server_inst)
|
57
|
+
@browser = ::Rack::MockRequest.new(rack.to_app)
|
58
|
+
end
|
60
59
|
end
|
61
60
|
|
62
61
|
Given /^the Server is running at "([^\"]*)"$/ do |app_path|
|
@@ -69,13 +68,13 @@ Given /^a template named "([^\"]*)" with:$/ do |name, string|
|
|
69
68
|
end
|
70
69
|
|
71
70
|
When /^I go to "([^\"]*)"$/ do |url|
|
72
|
-
|
71
|
+
cd(".") do
|
73
72
|
@last_response = @browser.get(URI.encode(url))
|
74
73
|
end
|
75
74
|
end
|
76
75
|
|
77
76
|
Then /^going to "([^\"]*)" should not raise an exception$/ do |url|
|
78
|
-
|
77
|
+
cd(".") do
|
79
78
|
last_response = nil
|
80
79
|
expect {
|
81
80
|
last_response = @browser.get(URI.encode(url))
|
@@ -85,61 +84,61 @@ Then /^going to "([^\"]*)" should not raise an exception$/ do |url|
|
|
85
84
|
end
|
86
85
|
|
87
86
|
Then /^the content type should be "([^\"]*)"$/ do |expected|
|
88
|
-
|
87
|
+
cd(".") do
|
89
88
|
expect(@last_response.content_type).to start_with(expected)
|
90
89
|
end
|
91
90
|
end
|
92
91
|
|
93
92
|
Then /^I should see "([^\"]*)"$/ do |expected|
|
94
|
-
|
93
|
+
cd(".") do
|
95
94
|
expect(@last_response.body).to include(expected)
|
96
95
|
end
|
97
96
|
end
|
98
97
|
|
99
98
|
Then /^I should see '([^\']*)'$/ do |expected|
|
100
|
-
|
99
|
+
cd(".") do
|
101
100
|
expect(@last_response.body).to include(expected)
|
102
101
|
end
|
103
102
|
end
|
104
103
|
|
105
104
|
Then /^I should see:$/ do |expected|
|
106
|
-
|
105
|
+
cd(".") do
|
107
106
|
expect(@last_response.body).to include(expected)
|
108
107
|
end
|
109
108
|
end
|
110
109
|
|
111
110
|
Then /^I should not see "([^\"]*)"$/ do |expected|
|
112
|
-
|
111
|
+
cd(".") do
|
113
112
|
expect(@last_response.body).to_not include(expected)
|
114
113
|
end
|
115
114
|
end
|
116
115
|
|
117
116
|
Then /^I should see content matching %r{(.*)}$/ do |expected|
|
118
|
-
|
117
|
+
cd(".") do
|
119
118
|
expect(@last_response.body).to match(expected)
|
120
119
|
end
|
121
120
|
end
|
122
121
|
|
123
122
|
Then /^I should not see content matching %r{(.*)}$/ do |expected|
|
124
|
-
|
123
|
+
cd(".") do
|
125
124
|
expect(@last_response.body).to_not match(expected)
|
126
125
|
end
|
127
126
|
end
|
128
127
|
|
129
128
|
Then /^I should not see:$/ do |expected|
|
130
|
-
|
129
|
+
cd(".") do
|
131
130
|
expect(@browser.last_response.body).to_not include(expected.chomp)
|
132
131
|
end
|
133
132
|
end
|
134
133
|
|
135
134
|
Then /^the status code should be "([^\"]*)"$/ do |expected|
|
136
|
-
|
135
|
+
cd(".") do
|
137
136
|
expect(@browser.last_response.status).to eq expected.to_i
|
138
137
|
end
|
139
138
|
end
|
140
139
|
|
141
140
|
Then /^I should see "([^\"]*)" lines$/ do |lines|
|
142
|
-
|
141
|
+
cd(".") do
|
143
142
|
expect(@last_response.body.chomp.split($/).length).to eq(lines.to_i)
|
144
143
|
end
|
145
144
|
end
|
@@ -128,7 +128,7 @@ module Middleman
|
|
128
128
|
return unless resource = sitemap.find_resource_by_destination_path(current_path)
|
129
129
|
|
130
130
|
# Look for partials relative to the current path
|
131
|
-
current_dir = resource.
|
131
|
+
current_dir = resource.file_descriptor[:relative_path].dirname
|
132
132
|
non_root = partial_path.to_s.sub(/^\//, '')
|
133
133
|
relative_dir = current_dir + Pathname(non_root)
|
134
134
|
|
@@ -138,7 +138,7 @@ module Middleman
|
|
138
138
|
partial_file = nil
|
139
139
|
|
140
140
|
[
|
141
|
-
[relative_dir.to_s, { preferred_engine: resource.
|
141
|
+
[relative_dir.to_s, { preferred_engine: resource.file_descriptor[:relative_path].extname[1..-1].to_sym }],
|
142
142
|
[non_root],
|
143
143
|
[non_root, { try_static: try_static }],
|
144
144
|
[relative_dir_no_underscore.to_s, { try_static: try_static }],
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# Core Pathname library used for traversal
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
# DbC
|
5
|
+
require 'middleman-core/contracts'
|
6
|
+
|
7
|
+
# Shared util methods
|
8
|
+
require 'middleman-core/util'
|
9
|
+
|
10
|
+
# Parsing YAML data
|
11
|
+
require 'yaml'
|
12
|
+
|
13
|
+
# Parsing JSON data
|
14
|
+
require 'active_support/json'
|
15
|
+
|
16
|
+
module Middleman
|
17
|
+
module Util
|
18
|
+
module Data
|
19
|
+
include Contracts
|
20
|
+
|
21
|
+
module_function
|
22
|
+
|
23
|
+
YAML_ERRORS = [StandardError]
|
24
|
+
|
25
|
+
# https://github.com/tenderlove/psych/issues/23
|
26
|
+
if defined?(Psych) && defined?(Psych::SyntaxError)
|
27
|
+
YAML_ERRORS << Psych::SyntaxError
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get the frontmatter and plain content from a file
|
31
|
+
# @param [String] path
|
32
|
+
# @return [Array<Hash, String>]
|
33
|
+
Contract Pathname, Maybe[Symbol] => [Hash, Maybe[String]]
|
34
|
+
def parse(full_path, known_type=nil)
|
35
|
+
data = {}
|
36
|
+
|
37
|
+
return [data, nil] if ::Middleman::Util.binary?(full_path)
|
38
|
+
|
39
|
+
# Avoid weird race condition when a file is renamed.
|
40
|
+
content = begin
|
41
|
+
File.read(full_path)
|
42
|
+
rescue ::EOFError
|
43
|
+
rescue ::IOError
|
44
|
+
rescue ::Errno::ENOENT
|
45
|
+
''
|
46
|
+
end
|
47
|
+
|
48
|
+
begin
|
49
|
+
if content =~ /\A.*coding:/
|
50
|
+
lines = content.split(/\n/)
|
51
|
+
lines.shift
|
52
|
+
content = lines.join("\n")
|
53
|
+
end
|
54
|
+
|
55
|
+
if known_type
|
56
|
+
if known_type == :yaml
|
57
|
+
result = parse_yaml(content, full_path, true)
|
58
|
+
elsif known_type == :json
|
59
|
+
result = parse_json(content, full_path)
|
60
|
+
end
|
61
|
+
else
|
62
|
+
result = parse_yaml(content, full_path, false)
|
63
|
+
end
|
64
|
+
|
65
|
+
return result if result
|
66
|
+
rescue
|
67
|
+
# Probably a binary file, move on
|
68
|
+
end
|
69
|
+
|
70
|
+
[data, content]
|
71
|
+
end
|
72
|
+
|
73
|
+
# Parse YAML frontmatter out of a string
|
74
|
+
# @param [String] content
|
75
|
+
# @return [Array<Hash, String>]
|
76
|
+
Contract String, Pathname, Bool => Maybe[[Hash, String]]
|
77
|
+
def parse_yaml(content, full_path, require_yaml=false)
|
78
|
+
total_delims = content.scan(/^(?:---|\.\.\.)\s*(?:\n|$)/).length
|
79
|
+
has_first_line_delim = !content.match(/\A(---\s*(?:\n|$))/).nil?
|
80
|
+
# has_closing_delim = (total_delims > 1 && has_first_line_delim) || (!has_first_line_delim && total_delims == 1)
|
81
|
+
|
82
|
+
parts = content.split(/^(?:---|\.\.\.)\s*(?:\n|$)/)
|
83
|
+
parts.shift if parts[0].empty?
|
84
|
+
|
85
|
+
yaml_string = nil
|
86
|
+
additional_content = nil
|
87
|
+
|
88
|
+
if require_yaml
|
89
|
+
yaml_string = parts[0]
|
90
|
+
additional_content = parts[1]
|
91
|
+
else
|
92
|
+
if total_delims > 1
|
93
|
+
if has_first_line_delim
|
94
|
+
yaml_string = parts[0]
|
95
|
+
additional_content = parts[1]
|
96
|
+
else
|
97
|
+
additional_content = content
|
98
|
+
end
|
99
|
+
else
|
100
|
+
additional_content = parts[0]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
return [{}, additional_content] if yaml_string.nil?
|
105
|
+
|
106
|
+
begin
|
107
|
+
data = map_value(::YAML.load(yaml_string) || {})
|
108
|
+
rescue *YAML_ERRORS => e
|
109
|
+
$stderr.puts "YAML Exception parsing #{full_path}: #{e.message}"
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
|
113
|
+
[data, additional_content]
|
114
|
+
rescue
|
115
|
+
[{}, additional_content]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Parse JSON frontmatter out of a string
|
119
|
+
# @param [String] content
|
120
|
+
# @return [Array<Hash, String>]
|
121
|
+
Contract String, Pathname => Maybe[[Hash, String]]
|
122
|
+
def parse_json(content, full_path)
|
123
|
+
begin
|
124
|
+
data = map_value(::ActiveSupport::JSON.decode(content))
|
125
|
+
rescue => e
|
126
|
+
$stderr.puts "JSON Exception parsing #{full_path}: #{e.message}"
|
127
|
+
return nil
|
128
|
+
end
|
129
|
+
|
130
|
+
[data, nil]
|
131
|
+
rescue
|
132
|
+
[{}, nil]
|
133
|
+
end
|
134
|
+
|
135
|
+
def symbolize_recursive(hash)
|
136
|
+
{}.tap do |h|
|
137
|
+
hash.each { |key, value| h[key.to_sym] = map_value(value) }
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def map_value(thing)
|
142
|
+
case thing
|
143
|
+
when Hash
|
144
|
+
symbolize_recursive(thing)
|
145
|
+
when Array
|
146
|
+
thing.map { |v| map_value(v) }
|
147
|
+
else
|
148
|
+
thing
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/lib/middleman-core/util.rb
CHANGED
@@ -11,8 +11,8 @@ require 'rack/mime'
|
|
11
11
|
# DbC
|
12
12
|
require 'middleman-core/contracts'
|
13
13
|
|
14
|
-
#
|
15
|
-
require '
|
14
|
+
# Indifferent Access
|
15
|
+
require 'hashie'
|
16
16
|
|
17
17
|
# For URI templating
|
18
18
|
require 'addressable/uri'
|
@@ -76,56 +76,25 @@ module Middleman
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
-
class
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
elsif key?(key.to_s)
|
84
|
-
super(key.to_s)
|
85
|
-
else
|
86
|
-
super
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def method_missing(key, *_args)
|
91
|
-
if key?(key.to_sym)
|
92
|
-
self[key.to_sym]
|
93
|
-
elsif key?(key.to_s)
|
94
|
-
self[key.to_s]
|
95
|
-
end
|
96
|
-
end
|
79
|
+
class EnhancedHash < ::Hashie::Mash
|
80
|
+
# include ::Hashie::Extensions::MergeInitializer
|
81
|
+
# include ::Hashie::Extensions::MethodReader
|
82
|
+
# include ::Hashie::Extensions::IndifferentAccess
|
97
83
|
end
|
98
84
|
|
99
|
-
# Recursively convert a normal Hash into a
|
85
|
+
# Recursively convert a normal Hash into a EnhancedHash
|
100
86
|
#
|
101
87
|
# @private
|
102
88
|
# @param [Hash] data Normal hash
|
103
|
-
# @return [
|
104
|
-
|
105
|
-
Contract Maybe[Or[String, Array, Hash, IndifferentHash]] => Maybe[FrozenDataStructure]
|
89
|
+
# @return [Hash]
|
90
|
+
Contract Maybe[Hash] => Maybe[Or[Array, EnhancedHash]]
|
106
91
|
def recursively_enhance(obj)
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
when IndifferentHash
|
112
|
-
obj.map { |key, value| [recursively_enhance(key), recursively_enhance(value)] }
|
113
|
-
when ::Array
|
114
|
-
res = obj.map { |element| recursively_enhance(element) }
|
115
|
-
Hamster::Vector.new(res)
|
116
|
-
when ::SortedSet
|
117
|
-
# This clause must go before ::Set clause, since ::SortedSet is a ::Set.
|
118
|
-
res = obj.map { |element| recursively_enhance(element) }
|
119
|
-
Hamster::SortedSet.new(res)
|
120
|
-
when ::Set
|
121
|
-
res = obj.map { |element| recursively_enhance(element) }
|
122
|
-
Hamster::Set.new(res)
|
123
|
-
when Hamster::Vector, Hamster::Set, Hamster::SortedSet
|
124
|
-
obj.map { |element| recursively_enhance(element) }
|
125
|
-
when ::TrueClass, ::FalseClass, ::Fixnum, ::Symbol, ::NilClass
|
126
|
-
obj
|
92
|
+
if obj.is_a? ::Array
|
93
|
+
obj.map { |e| recursively_enhance(e) }
|
94
|
+
elsif obj.is_a? ::Hash
|
95
|
+
::Hashie::Mash.new(obj)
|
127
96
|
else
|
128
|
-
obj
|
97
|
+
obj
|
129
98
|
end
|
130
99
|
end
|
131
100
|
|