middleman-core 4.0.0.alpha.6 → 4.0.0.beta.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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/features/asset_hash.feature +8 -1
  3. data/features/asset_host.feature +2 -13
  4. data/features/builder.feature +0 -2
  5. data/features/cli_init.feature +32 -0
  6. data/features/collections.feature +50 -0
  7. data/features/directory_index.feature +4 -5
  8. data/features/front-matter-neighbor.feature +20 -0
  9. data/features/helpers_link_to.feature +18 -0
  10. data/features/image_srcset_paths.feature +7 -0
  11. data/features/markdown_kramdown_in_haml.feature +2 -1
  12. data/features/minify_javascript.feature +1 -1
  13. data/features/multiple-sources.feature +8 -0
  14. data/fixtures/asset-hash-app/source/slim.html.slim +8 -0
  15. data/fixtures/asset-hash-app/source/subdir/index.html.erb +10 -1
  16. data/fixtures/asset-host-app/source/asset_host.html.erb +23 -1
  17. data/fixtures/collections-app/source/blog2/2011-01-01-new-article.html.markdown +2 -0
  18. data/fixtures/frontmatter-settings-neighbor-app/config.rb +19 -14
  19. data/fixtures/image-srcset-paths-app/image-srcset-paths.html.erb +1 -0
  20. data/fixtures/image-srcset-paths-app/images/blank.gif +0 -0
  21. data/fixtures/indexable-app/source/evil spaces.html +1 -1
  22. data/fixtures/large-build-app/config.rb +2 -0
  23. data/fixtures/large-build-app/source/spaces in file.html.erb +1 -1
  24. data/fixtures/more-traversal-app/source/layout.erb +1 -1
  25. data/fixtures/multiple-sources-with-duplicate-file-names-app/config.rb +2 -0
  26. data/fixtures/multiple-sources-with-duplicate-file-names-app/source/index.html.erb +1 -0
  27. data/fixtures/multiple-sources-with-duplicate-file-names-app/source2/index.html.erb +1 -0
  28. data/fixtures/traversal-app/source/.htaccess +0 -0
  29. data/fixtures/traversal-app/source/layout.erb +1 -1
  30. data/lib/middleman/rack.rb +1 -0
  31. data/lib/middleman-core/application.rb +15 -15
  32. data/lib/middleman-core/builder.rb +11 -7
  33. data/lib/middleman-core/cli/server.rb +86 -0
  34. data/lib/middleman-core/config_context.rb +1 -1
  35. data/lib/middleman-core/contracts.rb +0 -32
  36. data/lib/middleman-core/core_extensions/collections.rb +19 -17
  37. data/lib/middleman-core/core_extensions/data.rb +15 -17
  38. data/lib/middleman-core/core_extensions/default_helpers.rb +22 -1
  39. data/lib/middleman-core/core_extensions/file_watcher.rb +9 -7
  40. data/lib/middleman-core/core_extensions/front_matter.rb +2 -2
  41. data/lib/middleman-core/core_extensions/i18n.rb +11 -9
  42. data/lib/middleman-core/core_extensions/routing.rb +3 -4
  43. data/lib/middleman-core/extension.rb +179 -0
  44. data/lib/middleman-core/extension_manager.rb +8 -7
  45. data/lib/middleman-core/extensions/asset_hash.rb +14 -9
  46. data/lib/middleman-core/extensions/asset_host.rb +3 -1
  47. data/lib/middleman-core/extensions/gzip.rb +3 -2
  48. data/lib/middleman-core/extensions/lorem.rb +2 -2
  49. data/lib/middleman-core/extensions/relative_assets.rb +11 -5
  50. data/lib/middleman-core/file_renderer.rb +2 -2
  51. data/lib/middleman-core/logger.rb +1 -1
  52. data/lib/middleman-core/middleware/inline_url_rewriter.rb +9 -4
  53. data/lib/middleman-core/preview_server.rb +13 -9
  54. data/lib/middleman-core/rack.rb +2 -1
  55. data/lib/middleman-core/renderers/haml.rb +6 -0
  56. data/lib/middleman-core/renderers/kramdown.rb +1 -1
  57. data/lib/middleman-core/renderers/redcarpet.rb +1 -0
  58. data/lib/middleman-core/renderers/sass.rb +1 -1
  59. data/lib/middleman-core/renderers/slim.rb +1 -0
  60. data/lib/middleman-core/sitemap/extensions/ignores.rb +7 -4
  61. data/lib/middleman-core/sitemap/extensions/on_disk.rb +1 -1
  62. data/lib/middleman-core/sitemap/extensions/proxies.rb +13 -10
  63. data/lib/middleman-core/sitemap/extensions/redirects.rb +8 -7
  64. data/lib/middleman-core/sitemap/extensions/request_endpoints.rb +7 -6
  65. data/lib/middleman-core/sitemap/extensions/traversal.rb +3 -1
  66. data/lib/middleman-core/sitemap/resource.rb +14 -15
  67. data/lib/middleman-core/sitemap/store.rb +6 -5
  68. data/lib/middleman-core/sources/source_watcher.rb +29 -16
  69. data/lib/middleman-core/sources.rb +19 -21
  70. data/lib/middleman-core/step_definitions/builder_steps.rb +1 -1
  71. data/lib/middleman-core/step_definitions/server_steps.rb +3 -3
  72. data/lib/middleman-core/template_context.rb +8 -7
  73. data/lib/middleman-core/template_renderer.rb +2 -2
  74. data/lib/middleman-core/util.rb +57 -21
  75. data/lib/middleman-core/version.rb +1 -1
  76. data/lib/middleman-core.rb +2 -1
  77. data/middleman-core.gemspec +4 -1
  78. data/spec/middleman-core/core_extensions/data_spec.rb +41 -0
  79. data/spec/middleman-core/util_spec.rb +96 -0
  80. metadata +38 -8
  81. data/lib/middleman-core/util/hash_with_indifferent_access.rb +0 -103
  82. data/spec/middleman-core/binary_spec.rb +0 -15
  83. data/spec/middleman-core/path_match_spec.rb +0 -37
@@ -1,3 +1,4 @@
1
+ require 'forwardable'
1
2
  require 'active_support/core_ext/class/attribute'
2
3
  require 'middleman-core/configuration'
3
4
  require 'middleman-core/contracts'
@@ -81,6 +82,30 @@ module Middleman
81
82
  # @return [Array<Module>] a list of all the helper modules this extension provides. Set these using {#helpers}.
82
83
  class_attribute :defined_helpers, instance_reader: false, instance_writer: false
83
84
 
85
+ # @!attribute exposed_to_application
86
+ # @!scope class
87
+ # @api private
88
+ # @return [Hash<Symbol, Symbol>] a list of all the methods modules this extension exposes to app. Set these using {#expose_to_application}.
89
+ class_attribute :exposed_to_application, instance_reader: false, instance_writer: false
90
+
91
+ # @!attribute exposed_to_config
92
+ # @!scope class
93
+ # @api private
94
+ # @return [Hash<Symbol, Symbol>] a list of all the methods modules this extension exposes to config. Set these using {#expose_to_config}.
95
+ class_attribute :exposed_to_config, instance_reader: false, instance_writer: false
96
+
97
+ # @!attribute exposed_to_template
98
+ # @!scope class
99
+ # @api private
100
+ # @return [Hash<Symbol, Symbol>] a list of all the methods modules this extension exposes to templates. Set these using {#expose_to_template}.
101
+ class_attribute :exposed_to_template, instance_reader: false, instance_writer: false
102
+
103
+ # @!attribute exposed_to_template
104
+ # @!scope class
105
+ # @api private
106
+ # @return [Array<Any>] a list of method generators.
107
+ class_attribute :resources_generators, instance_reader: false, instance_writer: false
108
+
84
109
  # @!attribute ext_name
85
110
  # @!scope class
86
111
  # @return [Symbol] the name this extension is registered under. This is the symbol used to activate the extension.
@@ -110,6 +135,19 @@ module Middleman
110
135
  config.define_setting(key, default, description, options)
111
136
  end
112
137
 
138
+ # Short-hand for simple Sitemap manipulation
139
+ # @example A generator which returns an array of resources
140
+ # resources :make_resources
141
+ # @example A generator which maps a path to a method
142
+ # resources make_resource: :make_it
143
+ # @example A generator which maps a path to a string
144
+ # resources make_resource: 'Hello'
145
+ # @param [Array] generators The generator definitions
146
+ def resources(*generators)
147
+ self.resources_generators ||= []
148
+ self.resources_generators += generators
149
+ end
150
+
113
151
  # Declare helpers to be added the global Middleman application.
114
152
  # This accepts either a list of modules to add on behalf
115
153
  # of this extension, or a block whose contents will all
@@ -137,6 +175,68 @@ module Middleman
137
175
  self.defined_helpers += modules
138
176
  end
139
177
 
178
+ # Takes a method within this extension and exposes it globally
179
+ # on the main `app` instance. Used for very low-level extensions
180
+ # which many other extensions depend upon. Such as Data and
181
+ # File watching.
182
+ # @example with Hash:
183
+ # expose_to_application global_name: :local_name
184
+ # @example with Array:
185
+ # expose_to_application :method1, :method2
186
+ # @param [Array<Sumbol>, Hash<Symbol, Symbol>] symbols An optional list of symbols representing instance methods to exposed.
187
+ # @return [void]
188
+ def expose_to_application(*symbols)
189
+ self.exposed_to_application ||= {}
190
+
191
+ if symbols.first && symbols.first.is_a?(Hash)
192
+ self.exposed_to_application.merge!(symbols.first)
193
+ elsif symbols.is_a? Array
194
+ symbols.each do |sym|
195
+ self.exposed_to_application[sym] = sym
196
+ end
197
+ end
198
+ end
199
+
200
+ # Takes a method within this extension and exposes it inside the scope
201
+ # of the config.rb sandbox.
202
+ # @example with Hash:
203
+ # expose_to_config global_name: :local_name
204
+ # @example with Array:
205
+ # expose_to_config :method1, :method2
206
+ # @param [Array<Sumbol>, Hash<Symbol, Symbol>] symbols An optional list of symbols representing instance methods to exposed.
207
+ # @return [void]
208
+ def expose_to_config(*symbols)
209
+ self.exposed_to_config ||= {}
210
+
211
+ if symbols.first && symbols.first.is_a?(Hash)
212
+ self.exposed_to_config.merge!(symbols.first)
213
+ elsif symbols.is_a? Array
214
+ symbols.each do |sym|
215
+ self.exposed_to_config[sym] = sym
216
+ end
217
+ end
218
+ end
219
+
220
+ # Takes a method within this extension and exposes it inside the scope
221
+ # of the templating engine. Like `helpers`, but scoped.
222
+ # @example with Hash:
223
+ # expose_to_template global_name: :local_name
224
+ # @example with Array:
225
+ # expose_to_template :method1, :method2
226
+ # @param [Array<Sumbol>, Hash<Symbol, Symbol>] symbols An optional list of symbols representing instance methods to exposed.
227
+ # @return [void]
228
+ def expose_to_template(*symbols)
229
+ self.exposed_to_template ||= {}
230
+
231
+ if symbols.first && symbols.first.is_a?(Hash)
232
+ self.exposed_to_template.merge!(symbols.first)
233
+ elsif symbols.is_a? Array
234
+ symbols.each do |sym|
235
+ self.exposed_to_template[sym] = sym
236
+ end
237
+ end
238
+ end
239
+
140
240
  # Reset all {Extension.after_extension_activated} callbacks.
141
241
  # @api private
142
242
  # @return [void]
@@ -190,6 +290,7 @@ module Middleman
190
290
  @_helpers = []
191
291
  @app = app
192
292
 
293
+ expose_methods
193
294
  setup_options(options_hash, &block)
194
295
 
195
296
  # Bind app hooks to local methods
@@ -197,6 +298,7 @@ module Middleman
197
298
  bind_after_configuration
198
299
  bind_before_build
199
300
  bind_after_build
301
+ bind_ready
200
302
  end
201
303
 
202
304
  # @!method before_configuration
@@ -216,6 +318,10 @@ module Middleman
216
318
  # Respond to the `after_build` event.
217
319
  # If an `after_build` method is implemented, that method will be run after the builder runs.
218
320
 
321
+ # @!method ready
322
+ # Respond to the `ready` event.
323
+ # If an `ready` method is implemented, that method will be run after the app has finished booting up.
324
+
219
325
  # @!method manipulate_resource_list(resources)
220
326
  # Manipulate the resource list by transforming or adding {Sitemap::Resource}s.
221
327
  # Sitemap manipulation is a powerful way of interacting with a project, since it can modify each {Sitemap::Resource} or generate new {Sitemap::Resources}. This method is used in a pipeline where each sitemap manipulator is run in turn, with each one being fed the output of the previous manipulator. See the source of built-in Middleman extensions like {Middleman::Extensions::DirectoryIndexes} and {Middleman::Extensions::AssetHash} for examples of how to use this.
@@ -226,8 +332,28 @@ module Middleman
226
332
  # @param [Array<Sitemap::Resource>] resources A list of all the resources known to the sitemap.
227
333
  # @return [Array<Sitemap::Resource>] The transformed list of resources.
228
334
 
335
+ def add_exposed_to_context(context)
336
+ (self.class.exposed_to_template || {}).each do |k, v|
337
+ context.define_singleton_method(k, &method(v))
338
+ end
339
+ end
340
+
229
341
  private
230
342
 
343
+ def expose_methods
344
+ (self.class.exposed_to_application || {}).each do |k, v|
345
+ app.define_singleton_method(k, &method(v))
346
+ end
347
+
348
+ (self.class.exposed_to_config || {}).each do |k, v|
349
+ app.config_context.define_singleton_method(k, &method(v))
350
+ end
351
+
352
+ (self.class.defined_helpers || []).each do |m|
353
+ app.template_context_class.send(:include, m)
354
+ end
355
+ end
356
+
231
357
  # @yield An optional block that can be used to customize options before the extension is activated.
232
358
  # @yieldparam Middleman::Configuration::ConfigurationManager] options Extension options
233
359
  def setup_options(options_hash)
@@ -260,6 +386,53 @@ module Middleman
260
386
  if ext.respond_to?(:manipulate_resource_list)
261
387
  ext.app.sitemap.register_resource_list_manipulator(ext.class.ext_name, ext, ext.class.resource_list_manipulator_priority)
262
388
  end
389
+
390
+ if ext.class.resources_generators && !ext.class.resources_generators.empty?
391
+ ext.app.sitemap.register_resource_list_manipulator(
392
+ :"#{ext.class.ext_name}_generator",
393
+ ext,
394
+ ext.class.resource_list_manipulator_priority,
395
+ :generate_resources
396
+ )
397
+ end
398
+ end
399
+ end
400
+
401
+ def generate_resources(resources)
402
+ generator_defs = self.class.resources_generators.reduce({}) do |sum, g|
403
+ resource_definitions = if g.is_a? Hash
404
+ g
405
+ elsif g.is_a? Symbol
406
+ definition = method(g)
407
+
408
+ if definition.arity == 0
409
+ send(g)
410
+ else
411
+ send(g, resources)
412
+ end
413
+ else
414
+ {}
415
+ end
416
+
417
+ sum.merge(resource_definitions)
418
+ end
419
+
420
+ resources + generator_defs.map do |path, g|
421
+ if g.is_a? Symbol
422
+ definition = method(g)
423
+
424
+ g = if definition.arity == 0
425
+ send(g)
426
+ else
427
+ send(g, resources)
428
+ end
429
+ end
430
+
431
+ ::Middleman::Sitemap::StringResource.new(
432
+ app.sitemap,
433
+ path,
434
+ g
435
+ )
263
436
  end
264
437
  end
265
438
 
@@ -283,10 +456,16 @@ module Middleman
283
456
  @app.after_build do |builder|
284
457
  if ext.method(:after_build).arity == 1
285
458
  ext.after_build(builder)
459
+ elsif ext.method(:after_build).arity == 2
460
+ ext.after_build(builder, builder.thor)
286
461
  else
287
462
  ext.after_build
288
463
  end
289
464
  end
290
465
  end
466
+
467
+ def bind_ready
468
+ @app.ready(&method(:ready)) if respond_to?(:ready)
469
+ end
291
470
  end
292
471
  end
@@ -55,7 +55,7 @@ module Middleman
55
55
 
56
56
  def activate_all
57
57
  logger.debug 'Loaded extensions:'
58
- instances = @activated.each_with_object([]) do |(ext_name, ext), sum|
58
+ @instances = @activated.each_with_object([]) do |(ext_name, ext), sum|
59
59
  if ext.is_a?(Hash)
60
60
  ext.each do |instance_key, instance|
61
61
  logger.debug "== Extension: #{ext_name} #{instance_key}"
@@ -67,14 +67,15 @@ module Middleman
67
67
  end
68
68
  end
69
69
 
70
- instances.each do |ext|
71
- # Forward Extension helpers to TemplateContext
72
- Array(ext.class.defined_helpers).each do |m|
73
- @app.template_context_class.send(:include, m)
74
- end
75
-
70
+ @instances.each do |ext|
76
71
  ::Middleman::Extension.activated_extension(ext)
77
72
  end
78
73
  end
74
+
75
+ def add_exposed_to_context(context)
76
+ @instances.each do |ext|
77
+ ext.add_exposed_to_context(context)
78
+ end
79
+ end
79
80
  end
80
81
  end
@@ -1,7 +1,8 @@
1
+ require 'addressable/uri'
1
2
  require 'middleman-core/util'
2
3
 
3
4
  class Middleman::Extensions::AssetHash < ::Middleman::Extension
4
- option :exts, %w(.jpg .jpeg .png .gif .webp .js .css .otf .woff .eot .ttf .svg), 'List of extensions that get asset hashes appended to them.'
5
+ option :exts, %w(.jpg .jpeg .png .gif .webp .js .css .otf .woff .woff2 .eot .ttf .svg), 'List of extensions that get asset hashes appended to them.'
5
6
  option :ignore, [], 'Regexes of filenames to skip adding asset hashes to'
6
7
 
7
8
  def initialize(app, options_hash={}, &block)
@@ -9,7 +10,6 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
9
10
 
10
11
  require 'digest/sha1'
11
12
  require 'rack/mock'
12
- require 'uri'
13
13
  require 'middleman-core/middleware/inline_url_rewriter'
14
14
  end
15
15
 
@@ -28,7 +28,8 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
28
28
 
29
29
  Contract String, Or[String, Pathname], Any => Maybe[String]
30
30
  def rewrite_url(asset_path, dirpath, _request_path)
31
- relative_path = Pathname.new(asset_path).relative?
31
+ uri = ::Addressable::URI.parse(asset_path)
32
+ relative_path = uri.path[0..0] != '/'
32
33
 
33
34
  full_asset_path = if relative_path
34
35
  dirpath.join(asset_path).to_s
@@ -36,10 +37,11 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
36
37
  asset_path
37
38
  end
38
39
 
39
- return unless asset_page = app.sitemap.find_resource_by_path(full_asset_path)
40
+ return unless asset_page = app.sitemap.find_resource_by_destination_path(full_asset_path) || app.sitemap.find_resource_by_path(full_asset_path)
40
41
 
41
42
  replacement_path = "/#{asset_page.destination_path}"
42
43
  replacement_path = Pathname.new(replacement_path).relative_path_from(dirpath).to_s if relative_path
44
+
43
45
  replacement_path
44
46
  end
45
47
 
@@ -56,7 +58,7 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
56
58
  # This is so by the time we get around to the text files (which may reference
57
59
  # images and fonts) the static assets' hashes are already calculated.
58
60
  resources.sort_by do |a|
59
- if %w(.svg).include? a.ext
61
+ if %w(.svg .svgz).include? a.ext
60
62
  0
61
63
  elsif %w(.js .css).include? a.ext
62
64
  1
@@ -73,9 +75,10 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
73
75
  return if resource.ignored?
74
76
 
75
77
  # Render through the Rack interface so middleware and mounted apps get a shot
76
- response = @rack_client.get(URI.escape(resource.destination_path),
77
- 'bypass_inline_url_rewriter_asset_hash' => 'true'
78
- )
78
+ response = @rack_client.get(
79
+ URI.escape(resource.destination_path),
80
+ 'bypass_inline_url_rewriter_asset_hash' => 'true'
81
+ )
79
82
 
80
83
  raise "#{resource.path} should be in the sitemap!" unless response.status == 200
81
84
 
@@ -87,6 +90,8 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
87
90
 
88
91
  Contract IsA['Middleman::Sitemap::Resource'] => Bool
89
92
  def ignored_resource?(resource)
90
- @ignore.any? { |ignore| Middleman::Util.path_match(ignore, resource.destination_path) }
93
+ @ignore.any? do |ignore|
94
+ Middleman::Util.path_match(ignore, resource.destination_path)
95
+ end
91
96
  end
92
97
  end
@@ -1,3 +1,4 @@
1
+ require 'addressable/uri'
1
2
  require 'middleman-core/middleware/inline_url_rewriter'
2
3
 
3
4
  class Middleman::Extensions::AssetHost < ::Middleman::Extension
@@ -18,7 +19,8 @@ class Middleman::Extensions::AssetHost < ::Middleman::Extension
18
19
 
19
20
  Contract String, Or[String, Pathname], Any => String
20
21
  def rewrite_url(asset_path, dirpath, _request_path)
21
- relative_path = Pathname.new(asset_path).relative?
22
+ uri = ::Addressable::URI.parse(asset_path)
23
+ relative_path = uri.path[0..0] != '/'
22
24
 
23
25
  full_asset_path = if relative_path
24
26
  dirpath.join(asset_path).to_s
@@ -12,6 +12,7 @@
12
12
  class Middleman::Extensions::Gzip < ::Middleman::Extension
13
13
  option :exts, %w(.js .css .html .htm), 'File extensions to Gzip when building.'
14
14
  option :ignore, [], 'Patterns to avoid gzipping'
15
+ option :overwrite, false, 'Overwrite original files instead of adding .gz extension.'
15
16
 
16
17
  class NumberHelpers
17
18
  include ::Padrino::Helpers::NumberHelpers
@@ -73,11 +74,11 @@ class Middleman::Extensions::Gzip < ::Middleman::Extension
73
74
  Contract String => [Maybe[String], Maybe[Num], Maybe[Num]]
74
75
  def gzip_file(path)
75
76
  input_file = File.open(path, 'rb').read
76
- output_filename = path + '.gz'
77
+ output_filename = options.overwrite ? path : path + '.gz'
77
78
  input_file_time = File.mtime(path)
78
79
 
79
80
  # Check if the right file's already there
80
- if File.exist?(output_filename) && File.mtime(output_filename) == input_file_time
81
+ if !options.overwrite && File.exist?(output_filename) && File.mtime(output_filename) == input_file_time
81
82
  return [nil, nil, nil]
82
83
  end
83
84
 
@@ -152,8 +152,8 @@ class Middleman::Extensions::Lorem < ::Middleman::Extension
152
152
  color = options[:color]
153
153
 
154
154
  if options[:random_color]
155
- background_color = hex.shuffle[0...6].join
156
- color = hex.shuffle[0...6].join
155
+ background_color = hex.sample(6).join
156
+ color = hex.sample(6).join
157
157
  end
158
158
 
159
159
  src << "/#{background_color.sub(/^#/, '')}" if background_color
@@ -1,6 +1,8 @@
1
+ require 'addressable/uri'
2
+
1
3
  # Relative Assets extension
2
4
  class Middleman::Extensions::RelativeAssets < ::Middleman::Extension
3
- option :exts, %w(.css .png .jpg .jpeg .webp .svg .svgz .js .gif .ttf .otf .woff), 'List of extensions that get cache busters strings appended to them.'
5
+ option :exts, %w(.css .png .jpg .jpeg .webp .svg .svgz .js .gif .ttf .otf .woff .woff2), 'List of extensions that get cache busters strings appended to them.'
4
6
  option :sources, %w(.htm .html .css), 'List of extensions that are searched for relative assets.'
5
7
  option :ignore, [], 'Regexes of filenames to skip adding query strings to'
6
8
 
@@ -22,7 +24,11 @@ class Middleman::Extensions::RelativeAssets < ::Middleman::Extension
22
24
 
23
25
  Contract String, Or[String, Pathname], Any => Maybe[String]
24
26
  def rewrite_url(asset_path, dirpath, request_path)
25
- relative_path = Pathname.new(asset_path).relative?
27
+ uri = ::Addressable::URI.parse(asset_path)
28
+
29
+ return if uri.path[0..0] != '/'
30
+
31
+ relative_path = uri.host.nil?
26
32
 
27
33
  full_asset_path = if relative_path
28
34
  dirpath.join(asset_path).to_s
@@ -30,9 +36,9 @@ class Middleman::Extensions::RelativeAssets < ::Middleman::Extension
30
36
  asset_path
31
37
  end
32
38
 
33
- return unless !full_asset_path.include?('//') && !asset_path.start_with?('data:')
34
-
35
39
  current_dir = Pathname(request_path).dirname
36
- Pathname(full_asset_path).relative_path_from(current_dir).to_s
40
+ result = Pathname(full_asset_path).relative_path_from(current_dir).to_s
41
+
42
+ result
37
43
  end
38
44
  end
@@ -28,7 +28,7 @@ module Middleman
28
28
  # @param [Hash] opts
29
29
  # @param [Class] context
30
30
  # @return [String]
31
- Contract Hash, Hash, Any, Proc => String
31
+ Contract Hash, Hash, Any, Maybe[Proc] => String
32
32
  def render(locs={}, opts={}, context, &block)
33
33
  path = @path.dup
34
34
 
@@ -102,7 +102,7 @@ module Middleman
102
102
  # Get the template data from a path
103
103
  # @param [String] path
104
104
  # @return [String]
105
- Contract None => String
105
+ Contract String
106
106
  def template_data_for_file
107
107
  if @app.extensions[:front_matter]
108
108
  @app.extensions[:front_matter].template_data_for_file(@path) || ''
@@ -39,7 +39,7 @@ module Middleman
39
39
  def call(message, *args)
40
40
  return if @instrumenting.is_a?(String) && @instrumenting != 'instrument' && !message.include?(@instrumenting)
41
41
 
42
- evt = ActiveSupport::Notifications::Event.new(message, *args)
42
+ evt = ::ActiveSupport::Notifications::Event.new(message, *args)
43
43
  info "== Instrument (#{evt.name.sub(/.middleman$/, '')}): #{evt.duration}ms\n#{args.last}"
44
44
  end
45
45
  end
@@ -1,7 +1,8 @@
1
- require 'middleman-core/util'
2
- require 'middleman-core/contracts'
3
1
  require 'rack'
4
2
  require 'rack/response'
3
+ require 'addressable/uri'
4
+ require 'middleman-core/util'
5
+ require 'middleman-core/contracts'
5
6
 
6
7
  module Middleman
7
8
  module Middleware
@@ -51,10 +52,13 @@ module Middleman
51
52
 
52
53
  if path =~ /(^\/$)|(#{@source_exts_regex_text}$)/
53
54
  if body = ::Middleman::Util.extract_response_text(response)
55
+
54
56
  dirpath = Pathname.new(File.dirname(path))
55
57
 
56
58
  rewritten = ::Middleman::Util.rewrite_paths(body, path, @exts) do |asset_path|
57
- relative_path = Pathname.new(asset_path).relative?
59
+ uri = ::Addressable::URI.parse(asset_path)
60
+
61
+ relative_path = uri.host.nil?
58
62
 
59
63
  full_asset_path = if relative_path
60
64
  dirpath.join(asset_path).to_s
@@ -76,7 +80,7 @@ module Middleman
76
80
  [status, headers, response]
77
81
  end
78
82
 
79
- Contract IGNORE_DESCRIPTOR => Bool
83
+ Contract IGNORE_DESCRIPTOR, String => Bool
80
84
  def should_ignore?(validator, value)
81
85
  if validator.is_a? Regexp
82
86
  # Treat as Regexp
@@ -89,6 +93,7 @@ module Middleman
89
93
  File.fnmatch(value, validator)
90
94
  else
91
95
  # If some unknown thing, don't ignore
96
+ false
92
97
  end
93
98
  end
94
99
  end
@@ -6,8 +6,6 @@ require 'middleman-core/rack'
6
6
  # rubocop:disable GlobalVars
7
7
  module Middleman
8
8
  module PreviewServer
9
- DEFAULT_PORT = 4567
10
-
11
9
  class << self
12
10
  extend Forwardable
13
11
 
@@ -17,9 +15,7 @@ module Middleman
17
15
  # Start an instance of Middleman::Application
18
16
  # @return [void]
19
17
  def start(opts={})
20
- @options = opts.dup.freeze
21
- @host = @options[:host] || '0.0.0.0'
22
- @port = @options[:port] || DEFAULT_PORT
18
+ @options = opts
23
19
 
24
20
  mount_instance(new_app)
25
21
  logger.info "== The Middleman is standing watch at #{uri}"
@@ -75,6 +71,10 @@ module Middleman
75
71
  end
76
72
 
77
73
  unmount_instance
74
+
75
+ @webrick.shutdown
76
+ @webrick = nil
77
+
78
78
  mount_instance(app)
79
79
 
80
80
  logger.info '== The Middleman has reloaded'
@@ -90,7 +90,7 @@ module Middleman
90
90
  private
91
91
 
92
92
  def new_app
93
- opts = @options
93
+ opts = @options.dup
94
94
 
95
95
  ::Middleman::Logger.singleton(
96
96
  opts[:debug] ? 0 : 1,
@@ -103,6 +103,9 @@ module Middleman
103
103
  config[:watcher_force_polling] = opts[:force_polling]
104
104
  config[:watcher_latency] = opts[:latency]
105
105
 
106
+ config[:host] = opts[:host] if opts[:host]
107
+ config[:port] = opts[:port] if opts[:port]
108
+
106
109
  ready do
107
110
  match_against = [
108
111
  %r{^config\.rb$},
@@ -114,12 +117,13 @@ module Middleman
114
117
  # config.rb
115
118
  files.watch :reload,
116
119
  path: root,
117
- ignored: proc { |file|
118
- match_against.none? { |m| file[:relative_path].to_s.match(m) }
119
- }
120
+ only: match_against
120
121
  end
121
122
  end
122
123
 
124
+ @host = app.config[:host]
125
+ @port = app.config[:port]
126
+
123
127
  app.files.on_change :reload do
124
128
  $mm_reload = true
125
129
  @webrick.stop
@@ -82,6 +82,7 @@ module Middleman
82
82
  request_path.force_encoding('UTF-8')
83
83
  end
84
84
  request_path = ::Middleman::Util.full_path(request_path, @middleman)
85
+ full_request_path = File.join(env['SCRIPT_NAME'], request_path) # Path including rack mount
85
86
 
86
87
  # Run before callbacks
87
88
  @middleman.run_hook :before
@@ -90,7 +91,7 @@ module Middleman
90
91
  resource = @middleman.sitemap.find_resource_by_destination_path(request_path.gsub(' ', '%20'))
91
92
 
92
93
  # Return 404 if not in sitemap
93
- return not_found(res, request_path) unless resource && !resource.ignored?
94
+ return not_found(res, full_request_path) unless resource && !resource.ignored?
94
95
 
95
96
  # If this path is a binary file, send it immediately
96
97
  return send_file(resource, env) if resource.binary?
@@ -48,6 +48,12 @@ module Middleman
48
48
  # Add haml helpers to context
49
49
  ::Middleman::TemplateContext.send :include, ::Haml::Helpers
50
50
  end
51
+
52
+ def add_exposed_to_context(context)
53
+ super
54
+
55
+ context.init_haml_helpers if context.respond_to?(:init_haml_helpers)
56
+ end
51
57
  end
52
58
  end
53
59
  end
@@ -6,7 +6,7 @@ module Middleman
6
6
  class KramdownTemplate < ::Tilt::KramdownTemplate
7
7
  def evaluate(scope, *)
8
8
  @output ||= begin
9
- MiddlemanKramdownHTML.scope = ::Middleman::Renderers::Haml.last_haml_scope || scope
9
+ MiddlemanKramdownHTML.scope = (defined?(::Middleman::Renderers::Haml) && ::Middleman::Renderers::Haml.last_haml_scope) ? ::Middleman::Renderers::Haml.last_haml_scope : scope
10
10
 
11
11
  output, warnings = MiddlemanKramdownHTML.convert(@engine.root, @engine.options)
12
12
  @engine.warnings.concat(warnings)
@@ -1,4 +1,5 @@
1
1
  require 'redcarpet'
2
+ require 'active_support/core_ext/module/attribute_accessors'
2
3
 
3
4
  module Middleman
4
5
  module Renderers
@@ -105,7 +105,7 @@ module Middleman
105
105
  filename: eval_file,
106
106
  line: line,
107
107
  syntax: syntax,
108
- custom: { middleman_context: ctx.app }
108
+ custom: (options[:custom] || {}).merge(middleman_context: ctx.app)
109
109
  }
110
110
 
111
111
  if ctx.is_a?(::Middleman::TemplateContext) && file
@@ -24,6 +24,7 @@ module Middleman
24
24
  super
25
25
 
26
26
  # Setup Slim options to work with partials
27
+ ::Slim::Engine.disable_option_validator!
27
28
  ::Slim::Engine.set_options(
28
29
  buffer: '@_out_buf',
29
30
  use_html_safe: true,
@@ -3,12 +3,15 @@ module Middleman
3
3
  module Extensions
4
4
  # Class to handle managing ignores
5
5
  class Ignores < Extension
6
+ # Expose `create_ignore` as `app.ignore`
7
+ expose_to_application ignore: :create_ignore
8
+
9
+ # Expose `create_ignore` to config as `ignore`
10
+ expose_to_config ignore: :create_ignore
11
+
6
12
  def initialize(app, config={}, &block)
7
13
  super
8
14
 
9
- @app.add_to_config_context(:ignore, &method(:create_ignore))
10
- @app.define_singleton_method(:ignore, &method(:create_ignore))
11
-
12
15
  # Array of callbacks which can ass ignored
13
16
  @ignored_callbacks = Set.new
14
17
 
@@ -18,7 +21,7 @@ module Middleman
18
21
  # Ignore a path or add an ignore callback
19
22
  # @param [String, Regexp] path Path glob expression, or path regex
20
23
  # @return [void]
21
- Contract Maybe[Or[String, Regexp]], Proc => Any
24
+ Contract Maybe[Or[String, Regexp]], Maybe[Proc] => Any
22
25
  def create_ignore(path=nil, &block)
23
26
  if path.is_a? Regexp
24
27
  @ignored_callbacks << proc { |p| p =~ path }