middleman-core 4.0.0.alpha.6 → 4.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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 }