middleman-core-with-external-sources-watch-fix 4.1.0 → 4.1.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/features/asset_hash.feature +47 -10
  3. data/features/builder.feature +8 -4
  4. data/features/collections.feature +27 -0
  5. data/features/custom_layouts.feature +17 -1
  6. data/features/dynamic_pages.feature +15 -12
  7. data/features/front-matter-neighbor.feature +6 -2
  8. data/features/helpers_link_to.feature +11 -1
  9. data/features/i18n_link_to.feature +34 -34
  10. data/features/markdown_redcarpet_in_slim.feature +41 -0
  11. data/features/page-id.feature +26 -0
  12. data/features/partials.feature +17 -5
  13. data/features/relative_assets.feature +1 -1
  14. data/features/relative_assets_helpers_only.feature +123 -0
  15. data/features/sass_in_slim.feature +40 -0
  16. data/features/template-key-collision.feature +26 -0
  17. data/fixtures/asset-hash-minified-app/source/images/100px.jpg +0 -0
  18. data/fixtures/asset-hash-minified-app/source/stylesheets/test.css +3 -0
  19. data/fixtures/asset-hash-prefix/config.rb +7 -0
  20. data/fixtures/asset-hash-prefix/lib/middleware.rb +16 -0
  21. data/fixtures/asset-hash-prefix/source/index.html.erb +6 -0
  22. data/fixtures/asset-hash-prefix/source/javascripts/application.js +2 -0
  23. data/fixtures/asset-hash-prefix/source/javascripts/application.js.map +1 -0
  24. data/fixtures/asset-hash-prefix/source/layout.erb +17 -0
  25. data/fixtures/asset-hash-source-map/config.rb +6 -0
  26. data/fixtures/asset-hash-source-map/lib/middleware.rb +16 -0
  27. data/fixtures/asset-hash-source-map/source/index.html.erb +6 -0
  28. data/fixtures/asset-hash-source-map/source/javascripts/application.js +2 -0
  29. data/fixtures/asset-hash-source-map/source/javascripts/application.js.map +1 -0
  30. data/fixtures/asset-hash-source-map/source/layout.erb +17 -0
  31. data/fixtures/asset-host-app/source/images/blank0.gif +0 -0
  32. data/fixtures/asset-host-app/source/images/blank1.gif +0 -0
  33. data/fixtures/asset-host-app/source/images/blank10.gif +0 -0
  34. data/fixtures/asset-host-app/source/images/blank100.gif +0 -0
  35. data/fixtures/asset-host-app/source/images/blank101.gif +0 -0
  36. data/fixtures/asset-host-app/source/images/blank1010.gif +0 -0
  37. data/fixtures/asset-host-app/source/images/blank102.gif +0 -0
  38. data/fixtures/asset-host-app/source/images/blank1020.gif +0 -0
  39. data/fixtures/asset-host-app/source/images/blank1021.gif +0 -0
  40. data/fixtures/asset-host-app/source/images/blank1022.gif +0 -0
  41. data/fixtures/asset-host-app/source/images/blank1023.gif +0 -0
  42. data/fixtures/asset-host-app/source/images/blank1024.gif +0 -0
  43. data/fixtures/asset-host-app/source/images/blank103.gif +0 -0
  44. data/fixtures/asset-host-app/source/images/blank1030.gif +0 -0
  45. data/fixtures/asset-host-app/source/images/blank1031.gif +0 -0
  46. data/fixtures/asset-host-app/source/images/blank1032.gif +0 -0
  47. data/fixtures/asset-host-app/source/images/blank1033.gif +0 -0
  48. data/fixtures/asset-host-app/source/images/blank1034.gif +0 -0
  49. data/fixtures/asset-host-app/source/images/blank104.gif +0 -0
  50. data/fixtures/asset-host-app/source/images/blank1043.gif +0 -0
  51. data/fixtures/asset-host-app/source/images/blank1054.gif +0 -0
  52. data/fixtures/asset-host-app/source/images/blank2.gif +0 -0
  53. data/fixtures/asset-host-app/source/images/blank20.gif +0 -0
  54. data/fixtures/asset-host-app/source/images/blank21.gif +0 -0
  55. data/fixtures/asset-host-app/source/images/blank22.gif +0 -0
  56. data/fixtures/asset-host-app/source/images/blank23.gif +0 -0
  57. data/fixtures/asset-host-app/source/images/blank24.gif +0 -0
  58. data/fixtures/asset-host-app/source/images/blank3.gif +0 -0
  59. data/fixtures/asset-host-app/source/images/blank30.gif +0 -0
  60. data/fixtures/asset-host-app/source/images/blank31.gif +0 -0
  61. data/fixtures/asset-host-app/source/images/blank32.gif +0 -0
  62. data/fixtures/asset-host-app/source/images/blank33.gif +0 -0
  63. data/fixtures/asset-host-app/source/images/blank34.gif +0 -0
  64. data/fixtures/asset-host-app/source/images/blank4.gif +0 -0
  65. data/fixtures/asset-host-app/source/images/blank43.gif +0 -0
  66. data/fixtures/asset-host-app/source/images/blank54.gif +0 -0
  67. data/fixtures/dynamic-pages-app/config.rb +4 -0
  68. data/fixtures/dynamic-pages-app/source/should_be_ignored9.html +1 -0
  69. data/fixtures/external-pipeline-error/config.rb +5 -0
  70. data/fixtures/external-pipeline-error/source/javascripts/file.js +0 -0
  71. data/fixtures/page-id-app/config.rb +5 -0
  72. data/fixtures/page-id-app/source/fm.html.erb +5 -0
  73. data/fixtures/page-id-app/source/index.html.erb +6 -0
  74. data/fixtures/page-id-app/source/overwrites/from-default.html.erb +1 -0
  75. data/fixtures/page-id-app/source/overwrites/from-frontmatter.html.erb +5 -0
  76. data/fixtures/partials-app/source/_block.erb +3 -0
  77. data/fixtures/partials-app/source/block.html.erb +3 -0
  78. data/fixtures/partials-app/source/index_missing.html.erb +3 -0
  79. data/fixtures/relative-assets-app/source/images/blank2.gif +0 -0
  80. data/fixtures/sass-in-slim-app/config.rb +0 -0
  81. data/lib/middleman-core/application.rb +30 -4
  82. data/lib/middleman-core/builder.rb +8 -2
  83. data/lib/middleman-core/config_context.rb +5 -1
  84. data/lib/middleman-core/configuration.rb +1 -1
  85. data/lib/middleman-core/core_extensions/collections.rb +28 -17
  86. data/lib/middleman-core/core_extensions/collections/step_context.rb +14 -7
  87. data/lib/middleman-core/core_extensions/data.rb +4 -2
  88. data/lib/middleman-core/core_extensions/default_helpers.rb +1 -0
  89. data/lib/middleman-core/core_extensions/front_matter.rb +8 -6
  90. data/lib/middleman-core/core_extensions/i18n.rb +3 -1
  91. data/lib/middleman-core/core_extensions/inline_url_rewriter.rb +20 -35
  92. data/lib/middleman-core/core_extensions/routing.rb +6 -3
  93. data/lib/middleman-core/extension.rb +41 -6
  94. data/lib/middleman-core/extensions.rb +2 -2
  95. data/lib/middleman-core/extensions/asset_hash.rb +10 -5
  96. data/lib/middleman-core/extensions/asset_host.rb +4 -3
  97. data/lib/middleman-core/extensions/cache_buster.rb +2 -2
  98. data/lib/middleman-core/extensions/directory_indexes.rb +8 -2
  99. data/lib/middleman-core/extensions/external_pipeline.rb +54 -8
  100. data/lib/middleman-core/extensions/minify_css.rb +9 -1
  101. data/lib/middleman-core/extensions/minify_javascript.rb +8 -0
  102. data/lib/middleman-core/extensions/relative_assets.rb +38 -13
  103. data/lib/middleman-core/file_renderer.rb +4 -4
  104. data/lib/middleman-core/load_paths.rb +3 -1
  105. data/lib/middleman-core/logger.rb +1 -1
  106. data/lib/middleman-core/meta_pages/templates/index.html.erb +3 -1
  107. data/lib/middleman-core/preview_server.rb +25 -18
  108. data/lib/middleman-core/rack.rb +2 -0
  109. data/lib/middleman-core/renderers/redcarpet.rb +2 -2
  110. data/lib/middleman-core/renderers/sass_functions.rb +4 -6
  111. data/lib/middleman-core/renderers/slim.rb +1 -5
  112. data/lib/middleman-core/sitemap/extensions/import.rb +2 -2
  113. data/lib/middleman-core/sitemap/extensions/proxies.rb +4 -1
  114. data/lib/middleman-core/sitemap/extensions/redirects.rb +1 -2
  115. data/lib/middleman-core/sitemap/resource.rb +15 -5
  116. data/lib/middleman-core/sitemap/store.rb +33 -2
  117. data/lib/middleman-core/sources.rb +2 -1
  118. data/lib/middleman-core/sources/source_watcher.rb +17 -6
  119. data/lib/middleman-core/template_context.rb +26 -21
  120. data/lib/middleman-core/template_renderer.rb +22 -9
  121. data/lib/middleman-core/util/binary.rb +4 -5
  122. data/lib/middleman-core/util/data.rb +33 -18
  123. data/lib/middleman-core/util/files.rb +27 -24
  124. data/lib/middleman-core/util/paths.rb +74 -14
  125. data/lib/middleman-core/util/rack.rb +16 -6
  126. data/lib/middleman-core/version.rb +1 -1
  127. data/middleman-core.gemspec +11 -8
  128. data/spec/middleman-core/core_extensions/data_spec.rb +107 -1
  129. data/spec/middleman-core/util_spec.rb +37 -0
  130. metadata +196 -18
@@ -75,26 +75,36 @@ module Middleman
75
75
  Contract Bool
76
76
  def template?
77
77
  return false if file_descriptor.nil?
78
- !::Tilt[file_descriptor[:full_path].to_s].nil?
78
+ !::Middleman::Util.tilt_class(file_descriptor[:full_path].to_s).nil?
79
79
  end
80
80
 
81
81
  # Backwards compatible method for turning descriptor into a string.
82
82
  # @return [String]
83
- Contract String
83
+ Contract Maybe[String]
84
84
  def source_file
85
85
  file_descriptor && file_descriptor[:full_path].to_s
86
86
  end
87
87
 
88
+ Contract Or[Symbol, String, Fixnum]
89
+ def page_id
90
+ metadata[:page][:id] || destination_path
91
+ end
92
+
88
93
  # Merge in new metadata specific to this resource.
89
94
  # @param [Hash] meta A metadata block with keys :options, :locals, :page.
90
95
  # Options are generally rendering/sitemap options
91
96
  # Locals are local variables for rendering this resource's template
92
97
  # Page are data that is exposed through this resource's data member.
93
98
  # Note: It is named 'page' for backwards compatibility with older MM.
94
- Contract METADATA_HASH => METADATA_HASH
95
- def add_metadata(meta={})
99
+ Contract METADATA_HASH, Maybe[Bool] => METADATA_HASH
100
+ def add_metadata(meta={}, reverse=false)
96
101
  @page_data = nil
97
- @metadata.deep_merge!(meta)
102
+
103
+ @metadata = if reverse
104
+ meta.deep_merge(@metadata)
105
+ else
106
+ @metadata.deep_merge(meta)
107
+ end
98
108
  end
99
109
 
100
110
  # Data about this resource, populated from frontmatter or extensions.
@@ -87,6 +87,13 @@ module Middleman
87
87
  @app.config_context.class.send :def_delegator, :app, :sitemap
88
88
  end
89
89
 
90
+ Contract Symbol, RespondTo[:manipulate_resource_list], Maybe[Or[Num, ArrayOf[Num]]], Maybe[Symbol] => Any
91
+ def register_resource_list_manipulators(name, manipulator, priority=50, custom_name=nil)
92
+ Array(priority || 50).each do |p|
93
+ register_resource_list_manipulator(name, manipulator, p, custom_name)
94
+ end
95
+ end
96
+
90
97
  # Register an object which can transform the sitemap resource list. Best to register
91
98
  # these in a `before_configuration` or `after_configuration` hook.
92
99
  #
@@ -95,7 +102,7 @@ module Middleman
95
102
  # @param [Numeric] priority Sets the order of this resource list manipulator relative to the rest. By default this is 50, and manipulators run in the order they are registered, but if a priority is provided then this will run ahead of or behind other manipulators.
96
103
  # @param [Symbol] custom_name The method name to execute.
97
104
  # @return [void]
98
- Contract Symbol, RespondTo[:manipulate_resource_list], Maybe[Num], Maybe[Symbol] => Any
105
+ Contract Symbol, RespondTo[:manipulate_resource_list], Maybe[Num, Bool], Maybe[Symbol] => Any
99
106
  def register_resource_list_manipulator(name, manipulator, priority=50, custom_name=nil)
100
107
  # The third argument used to be a boolean - handle those who still pass one
101
108
  priority = 50 unless priority.is_a? Numeric
@@ -149,6 +156,17 @@ module Middleman
149
156
  end
150
157
  end
151
158
 
159
+ # Find a resource given its page id
160
+ # @param [String] page_id The page id.
161
+ # @return [Middleman::Sitemap::Resource]
162
+ Contract Or[String, Symbol] => Maybe[IsA['Middleman::Sitemap::Resource']]
163
+ def find_resource_by_page_id(page_id)
164
+ @lock.synchronize do
165
+ ensure_resource_list_updated!
166
+ @_lookup_by_page_id[page_id.to_sym]
167
+ end
168
+ end
169
+
152
170
  # Get the array of all resources
153
171
  # @param [Boolean] include_ignored Whether to include ignored resources
154
172
  # @return [Array<Middleman::Sitemap::Resource>]
@@ -198,6 +216,8 @@ module Middleman
198
216
  # rebuild_resource_list! since the last time it was run. This is
199
217
  # very expensive!
200
218
  def ensure_resource_list_updated!
219
+ return if @app.config[:disable_sitemap]
220
+
201
221
  @lock.synchronize do
202
222
  return unless @needs_sitemap_rebuild
203
223
 
@@ -210,7 +230,7 @@ module Middleman
210
230
 
211
231
  @resource_list_manipulators.each do |m|
212
232
  ::Middleman::Util.instrument 'sitemap.manipulator', name: m[:name] do
213
- @app.logger.debug "== Running manipulator: #{m[:name]}"
233
+ @app.logger.debug "== Running manipulator: #{m[:name]} (#{m[:priority]})"
214
234
  @resources = m[:manipulator].send(m[:custom_name] || :manipulate_resource_list, @resources)
215
235
 
216
236
  # Reset lookup cache
@@ -219,9 +239,19 @@ module Middleman
219
239
  # Rebuild cache
220
240
  @resources.each do |resource|
221
241
  @_lookup_by_path[resource.path] = resource
242
+ end
243
+
244
+ @resources.each do |resource|
222
245
  @_lookup_by_destination_path[resource.destination_path] = resource
223
246
  end
224
247
 
248
+ # NB: This needs to be done after the previous two steps,
249
+ # since some proxy resources are looked up by path in order to
250
+ # get their metadata and subsequently their page_id.
251
+ @resources.each do |resource|
252
+ @_lookup_by_page_id[resource.page_id.to_sym] = resource
253
+ end
254
+
225
255
  invalidate_resources_not_ignored_cache!
226
256
  end
227
257
  end
@@ -239,6 +269,7 @@ module Middleman
239
269
  @lock.synchronize do
240
270
  @_lookup_by_path = {}
241
271
  @_lookup_by_destination_path = {}
272
+ @_lookup_by_page_id = {}
242
273
  end
243
274
  end
244
275
 
@@ -116,7 +116,8 @@ module Middleman
116
116
  Contract Or[Symbol, HANDLER], Maybe[Hash] => HANDLER
117
117
  def watch(type_or_handler, options={})
118
118
  handler = if type_or_handler.is_a? Symbol
119
- SourceWatcher.new(self, type_or_handler, options.delete(:path), options)
119
+ path = File.expand_path(options.delete(:path), app.root)
120
+ SourceWatcher.new(self, type_or_handler, path, options)
120
121
  else
121
122
  type_or_handler
122
123
  end
@@ -2,6 +2,7 @@
2
2
  require 'listen'
3
3
  require 'middleman-core/contracts'
4
4
  require 'digest'
5
+ require 'set'
5
6
 
6
7
  # Monkey patch Listen silencer so `only` works on directories too
7
8
  module Listen
@@ -49,7 +50,7 @@ module Middleman
49
50
  # Reference to lower level listener
50
51
  attr_reader :listener
51
52
 
52
- IGNORED_DIRECTORIES = %w(.git node_modules .sass-cache).freeze
53
+ IGNORED_DIRECTORIES = Set.new(%w(.git node_modules .sass-cache vendor/bundle .bundle))
53
54
 
54
55
  # Construct a new SourceWatcher
55
56
  #
@@ -92,7 +93,7 @@ module Middleman
92
93
  # @return [void]
93
94
  Contract String => Any
94
95
  def update_path(directory)
95
- @directory = Pathname(directory)
96
+ @directory = Pathname(File.expand_path(directory, app.root))
96
97
 
97
98
  stop_listener! if @listener
98
99
 
@@ -139,6 +140,7 @@ module Middleman
139
140
  end
140
141
 
141
142
  p = @directory + p if p.relative?
143
+
142
144
  if glob
143
145
  @extensionless_files[p]
144
146
  else
@@ -167,12 +169,13 @@ module Middleman
167
169
  wait_for_delay: 0.5
168
170
  }
169
171
 
170
- config[:latency] = @latency if @latency
172
+ config[:latency] = @latency.to_i if @latency
171
173
 
172
174
  @listener = ::Listen.to(@directory.to_s, config, &method(:on_listener_change))
173
175
 
174
176
  @listener.ignore(/^\.sass-cache/)
175
- # @listener.only(@only) unless @only.empty?
177
+ @listener.ignore(/^node_modules/)
178
+ @listener.ignore(/^vendor\/bundle/)
176
179
 
177
180
  @listener.start
178
181
  end
@@ -227,7 +230,8 @@ module Middleman
227
230
 
228
231
  Contract Pathname => Bool
229
232
  def should_not_recurse?(p)
230
- IGNORED_DIRECTORIES.include?(p.basename.to_s)
233
+ relative_path = p.relative_path_from(@directory).to_s
234
+ IGNORED_DIRECTORIES.include?(relative_path)
231
235
  end
232
236
 
233
237
  # The `listen` gem callback.
@@ -260,7 +264,8 @@ module Middleman
260
264
  logger.debug "== Change (#{f[:types].inspect}): #{f[:relative_path]}"
261
265
  end
262
266
 
263
- related_updates = ::Middleman::Util.find_related_files(app, (updated_paths + removed_paths)).select(&method(:valid?))
267
+ related_sources = valid_updates.map { |u| u[:full_path] } + removed_paths
268
+ related_updates = ::Middleman::Util.find_related_files(app, related_sources).select(&method(:valid?))
264
269
 
265
270
  related_updates.each do |f|
266
271
  logger.debug "== Possible Change (#{f[:types].inspect}): #{f[:relative_path]}"
@@ -299,9 +304,15 @@ module Middleman
299
304
  relative_path = path.relative_path_from(directory)
300
305
  relative_path = File.join(destination_dir, relative_path) if destination_dir
301
306
 
307
+ types << :no_frontmatter if partial?(relative_path.to_s)
308
+
302
309
  ::Middleman::SourceFile.new(Pathname(relative_path), path, directory, types, 0)
303
310
  end
304
311
 
312
+ def partial?(relative_path)
313
+ relative_path.split(::File::SEPARATOR).any? { |p| p.start_with?('_') }
314
+ end
315
+
305
316
  Contract IsA['Middleman::SourceFile'] => Any
306
317
  def record_file_change(f)
307
318
  if @files[f[:full_path]]
@@ -23,7 +23,7 @@ module Middleman
23
23
  attr_accessor :current_engine
24
24
 
25
25
  # Shorthand references to global values on the app instance.
26
- def_delegators :@app, :config, :logger, :sitemap, :server?, :build?, :environment?, :data, :extensions, :root
26
+ def_delegators :@app, :config, :logger, :sitemap, :server?, :build?, :environment?, :environment, :data, :extensions, :root, :development?, :production?
27
27
 
28
28
  # Initialize a context with the current app and predefined locals and options hashes.
29
29
  #
@@ -84,6 +84,7 @@ module Middleman
84
84
  # Reset stored buffer, regardless of success
85
85
  restore_buffer(buf_was)
86
86
  end
87
+
87
88
  # Render the layout, with the contents of the block inside.
88
89
  concat_safe_content render_file(layout_file, @locs, @opts) { content }
89
90
  ensure
@@ -96,14 +97,14 @@ module Middleman
96
97
  #
97
98
  # @param [String, Symbol] name The partial to render.
98
99
  # @param [Hash] options
100
+ # @param [Proc] block A block will be evaluated to return internal contents.
99
101
  # @return [String]
100
- Contract Any, Or[Symbol, String], Hash => String
102
+ Contract Any, Or[Symbol, String], Hash => String, Maybe[Proc] => String
101
103
  def render(_, name, options={}, &block)
102
104
  name = name.to_s
103
105
 
104
106
  partial_file = locate_partial(name, false) || locate_partial(name, true)
105
107
 
106
- return '' unless partial_file
107
108
  raise ::Middleman::TemplateRenderer::TemplateNotFound, "Could not locate partial: #{name}" unless partial_file
108
109
 
109
110
  source_path = sitemap.file_to_path(partial_file)
@@ -126,30 +127,34 @@ module Middleman
126
127
  # @return [String]
127
128
  Contract String, Maybe[Bool] => Maybe[IsA['Middleman::SourceFile']]
128
129
  def locate_partial(partial_path, try_static=true)
129
- return unless resource = sitemap.find_resource_by_destination_path(current_path)
130
-
131
- # Look for partials relative to the current path
132
- current_dir = resource.file_descriptor[:relative_path].dirname
133
- non_root = partial_path.to_s.sub(/^\//, '')
134
- relative_dir = current_dir + Pathname(non_root)
135
-
130
+ partial_file = nil
131
+ lookup_stack = []
132
+ non_root = partial_path.to_s.sub(/^\//, '')
136
133
  non_root_no_underscore = non_root.sub(/^_/, '').sub(/\/_/, '/')
137
- relative_dir_no_underscore = current_dir + Pathname(non_root_no_underscore)
138
134
 
139
- partial_file = nil
135
+ if resource = current_resource
136
+ current_dir = resource.file_descriptor[:relative_path].dirname
137
+ relative_dir = current_dir + Pathname(non_root)
138
+ relative_dir_no_underscore = current_dir + Pathname(non_root_no_underscore)
139
+ end
140
140
 
141
- [
142
- [relative_dir.to_s, { preferred_engine: resource.file_descriptor[:relative_path].extname[1..-1].to_sym }],
143
- [non_root],
144
- [non_root, { try_static: try_static }],
145
- [relative_dir_no_underscore.to_s, { try_static: try_static }],
146
- [non_root_no_underscore, { try_static: try_static }]
147
- ].each do |args|
141
+ lookup_stack.push [relative_dir.to_s,
142
+ { preferred_engine: resource.file_descriptor[:relative_path]
143
+ .extname[1..-1].to_sym }] if relative_dir
144
+ lookup_stack.push [non_root]
145
+ lookup_stack.push [non_root,
146
+ { try_static: try_static }]
147
+ lookup_stack.push [relative_dir_no_underscore.to_s,
148
+ { try_static: try_static }] if relative_dir_no_underscore
149
+ lookup_stack.push [non_root_no_underscore,
150
+ { try_static: try_static }]
151
+
152
+ lookup_stack.each do |args|
148
153
  partial_file = ::Middleman::TemplateRenderer.resolve_template(@app, *args)
149
154
  break if partial_file
150
155
  end
151
156
 
152
- partial_file || nil
157
+ partial_file
153
158
  end
154
159
 
155
160
  def current_path
@@ -185,7 +190,7 @@ module Middleman
185
190
  # handles cases like `style.css.sass.erb`
186
191
  content = nil
187
192
 
188
- while ::Tilt[path]
193
+ while ::Middleman::Util.tilt_class(path)
189
194
  begin
190
195
  opts[:template_body] = content if content
191
196
 
@@ -61,7 +61,7 @@ module Middleman
61
61
 
62
62
  # If we're specifically looking for a preferred engine
63
63
  if options.key?(:preferred_engine)
64
- extension_class = ::Tilt[options[:preferred_engine]]
64
+ extension_class = ::Middleman::Util.tilt_class(options[:preferred_engine])
65
65
 
66
66
  # Get a list of extensions for a preferred engine
67
67
  preferred_engines += ::Tilt.mappings.select do |_, engines|
@@ -89,7 +89,7 @@ module Middleman
89
89
  app.files.find(:source, path_with_ext, globbing)
90
90
  end
91
91
 
92
- found_template = file if file && (preferred_engine.nil? || ::Tilt[file[:full_path]])
92
+ found_template = file if file && (preferred_engine.nil? || ::Middleman::Util.tilt_class(file[:full_path].to_s))
93
93
  break if found_template
94
94
  end
95
95
 
@@ -133,20 +133,33 @@ module Middleman
133
133
  # Add extension helpers to context.
134
134
  @app.extensions.add_exposed_to_context(context)
135
135
 
136
+ locals.each do |k, _|
137
+ next unless context.respond_to?(k) && ![:current_path, :paginate, :page_articles, :blog_controller, :lang, :locale].include?(k.to_sym)
138
+
139
+ msg = "Template local `#{k}` tried to overwrite an existing context value. Please rename the key when passing to `locals`"
140
+
141
+ if @app.build?
142
+ throw msg
143
+ else
144
+ @app.logger.error(msg)
145
+ end
146
+ end
147
+
136
148
  content = ::Middleman::Util.instrument 'builder.output.resource.render-template', path: File.basename(path) do
137
- _render_with_all_renderers(path, locs, context, opts, &block)
149
+ _render_with_all_renderers(path, locals, context, options, &block)
138
150
  end
139
151
 
140
152
  # If we need a layout and have a layout, use it
141
153
  layout_file = fetch_layout(engine, options)
142
154
  if layout_file
143
- content = ::Middleman::Util.instrument 'builder.output.resource.render-layout', path: File.basename(layout_file[:relative_path].to_s) do
144
- if layout_file = fetch_layout(engine, options)
145
- layout_renderer = ::Middleman::FileRenderer.new(@app, layout_file[:relative_path].to_s)
155
+ content = if layout_file = fetch_layout(engine, options)
156
+ layout_renderer = ::Middleman::FileRenderer.new(@app, layout_file[:relative_path].to_s)
157
+
158
+ ::Middleman::Util.instrument 'builder.output.resource.render-layout', path: File.basename(layout_file[:relative_path].to_s) do
146
159
  layout_renderer.render(locals, options, context) { content }
147
- else
148
- content
149
160
  end
161
+ else
162
+ content
150
163
  end
151
164
  end
152
165
 
@@ -165,7 +178,7 @@ module Middleman
165
178
  # handles cases like `style.css.sass.erb`
166
179
  content = nil
167
180
 
168
- while ::Tilt[path]
181
+ while ::Middleman::Util.tilt_class(path)
169
182
  begin
170
183
  opts[:template_body] = content if content
171
184
 
@@ -47,14 +47,13 @@ module Middleman
47
47
  # @return [Boolean]
48
48
  Contract String => Bool
49
49
  def nonbinary_mime?(mime)
50
- case
51
- when mime.start_with?('text/')
50
+ if mime.start_with?('text/')
52
51
  true
53
- when mime.include?('xml') && !mime.include?('officedocument')
52
+ elsif mime.include?('xml') && !mime.include?('officedocument')
54
53
  true
55
- when mime.include?('json')
54
+ elsif mime.include?('json')
56
55
  true
57
- when mime.include?('javascript')
56
+ elsif mime.include?('javascript')
58
57
  true
59
58
  else
60
59
  false
@@ -3,6 +3,7 @@ require 'json'
3
3
  require 'pathname'
4
4
  require 'backports/2.1.0/array/to_h'
5
5
  require 'hashie'
6
+ require 'memoist'
6
7
 
7
8
  require 'middleman-core/util/binary'
8
9
  require 'middleman-core/contracts'
@@ -29,13 +30,14 @@ module Middleman
29
30
  if obj.is_a? ::Array
30
31
  obj.map { |e| recursively_enhance(e) }
31
32
  elsif obj.is_a? ::Hash
32
- ::Hashie::Mash.new(obj)
33
+ EnhancedHash.new(obj)
33
34
  else
34
35
  obj
35
36
  end
36
37
  end
37
38
 
38
39
  module Data
40
+ extend Memoist
39
41
  include Contracts
40
42
 
41
43
  module_function
@@ -55,20 +57,7 @@ module Middleman
55
57
  return [{}, nil]
56
58
  end
57
59
 
58
- start_delims, stop_delims = frontmatter_delims
59
- .values
60
- .flatten(1)
61
- .transpose
62
- .map(&::Regexp.method(:union))
63
-
64
- match = /
65
- \A(?:[^\r\n]*coding:[^\r\n]*\r?\n)?
66
- (?<start>#{start_delims})[ ]*\r?\n
67
- (?<frontmatter>.*?)[ ]*\r?\n?
68
- ^(?<stop>#{stop_delims})[ ]*\r?\n?
69
- \r?\n?
70
- (?<additional_content>.*)
71
- /mx.match(content) || {}
60
+ match = build_regex(frontmatter_delims).match(content) || {}
72
61
 
73
62
  unless match[:frontmatter]
74
63
  case known_type
@@ -98,27 +87,53 @@ module Middleman
98
87
  end
99
88
  end
100
89
 
90
+ def build_regex(frontmatter_delims)
91
+ start_delims, stop_delims = frontmatter_delims
92
+ .values
93
+ .flatten(1)
94
+ .transpose
95
+ .map(&::Regexp.method(:union))
96
+
97
+ /
98
+ \A(?:[^\r\n]*coding:[^\r\n]*\r?\n)?
99
+ (?<start>#{start_delims})[ ]*\r?\n
100
+ (?<frontmatter>.*?)[ ]*\r?\n?
101
+ ^(?<stop>#{stop_delims})[ ]*\r?\n?
102
+ \r?\n?
103
+ (?<additional_content>.*)
104
+ /mx
105
+ end
106
+ memoize :build_regex
107
+
101
108
  # Parse YAML frontmatter out of a string
102
109
  # @param [String] content
103
110
  # @return [Hash]
104
- Contract String, Pathname, Bool => Hash
111
+ Contract String, Pathname => Hash
105
112
  def parse_yaml(content, full_path)
106
- symbolize_recursive(::YAML.load(content) || {})
113
+ c = ::Middleman::Util.instrument 'parse.yaml' do
114
+ ::YAML.load(content)
115
+ end
116
+ c ? symbolize_recursive(c) : {}
107
117
  rescue StandardError, ::Psych::SyntaxError => error
108
118
  warn "YAML Exception parsing #{full_path}: #{error.message}"
109
119
  {}
110
120
  end
121
+ memoize :parse_yaml
111
122
 
112
123
  # Parse JSON frontmatter out of a string
113
124
  # @param [String] content
114
125
  # @return [Hash]
115
126
  Contract String, Pathname => Hash
116
127
  def parse_json(content, full_path)
117
- symbolize_recursive(::JSON.parse(content) || {})
128
+ c = ::Middleman::Util.instrument 'parse.json' do
129
+ ::JSON.parse(content)
130
+ end
131
+ c ? symbolize_recursive(c) : {}
118
132
  rescue StandardError => error
119
133
  warn "JSON Exception parsing #{full_path}: #{error.message}"
120
134
  {}
121
135
  end
136
+ memoize :parse_json
122
137
 
123
138
  def symbolize_recursive(value)
124
139
  case value