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
@@ -22,7 +22,7 @@ module Middleman
22
22
  end
23
23
  end
24
24
 
25
- Contract None => Any
25
+ Contract Any
26
26
  def before_configuration
27
27
  app.files.on_change(:source, &method(:update_files))
28
28
  end
@@ -7,12 +7,15 @@ 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
+ # Expose `create_proxy` as `app.proxy`
11
+ expose_to_application proxy: :create_proxy
12
+
13
+ # Expose `create_proxy` to config as `proxy`
14
+ expose_to_config proxy: :create_proxy
15
+
10
16
  def initialize(app, config={}, &block)
11
17
  super
12
18
 
13
- @app.add_to_config_context(:proxy, &method(:create_proxy))
14
- @app.define_singleton_method(:proxy, &method(:create_proxy))
15
-
16
19
  @proxy_configs = Set.new
17
20
  @post_config = false
18
21
  end
@@ -71,10 +74,10 @@ module Middleman
71
74
  ProxyResource.new(app.sitemap, path, target).tap do |p|
72
75
  md = metadata.dup
73
76
  p.add_metadata(
74
- locals: md.delete(:locals) || {},
75
- page: md.delete(:data) || {},
76
- options: md
77
- )
77
+ locals: md.delete(:locals) || {},
78
+ page: md.delete(:data) || {},
79
+ options: md
80
+ )
78
81
  end
79
82
  end
80
83
  end
@@ -96,7 +99,7 @@ module Middleman
96
99
  # The resource for the page this page is proxied to. Throws an exception
97
100
  # if there is no resource.
98
101
  # @return [Sitemap::Resource]
99
- Contract None => IsA['Middleman::Sitemap::Resource']
102
+ Contract IsA['Middleman::Sitemap::Resource']
100
103
  def target_resource
101
104
  resource = @store.find_resource_by_path(@target)
102
105
 
@@ -111,12 +114,12 @@ module Middleman
111
114
  resource
112
115
  end
113
116
 
114
- Contract None => IsA['Middleman::SourceFile']
117
+ Contract IsA['Middleman::SourceFile']
115
118
  def source_file
116
119
  target_resource.source_file
117
120
  end
118
121
 
119
- Contract None => Maybe[String]
122
+ Contract Maybe[String]
120
123
  def content_type
121
124
  mime_type = super
122
125
  return mime_type if mime_type
@@ -7,18 +7,19 @@ 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
+ # Expose `create_redirect` to config as `redirect`
11
+ expose_to_config redirect: :create_redirect
12
+
10
13
  def initialize(app, config={}, &block)
11
14
  super
12
15
 
13
- @app.add_to_config_context(:redirect, &method(:create_redirect))
14
-
15
16
  @redirects = {}
16
17
  end
17
18
 
18
19
  # Setup a redirect from a path to a target
19
20
  # @param [String] path
20
21
  # @param [Hash] opts The :to value gives a target path
21
- Contract String, ({ to: Or[String, IsA['Middleman::Sitemap::Resource']] }), Proc => Any
22
+ Contract String, ({ to: Or[String, IsA['Middleman::Sitemap::Resource']] }), Maybe[Proc] => Any
22
23
  def create_redirect(path, opts={}, &block)
23
24
  opts[:template] = block if block_given?
24
25
 
@@ -44,7 +45,7 @@ module Middleman
44
45
  end
45
46
 
46
47
  class RedirectResource < ::Middleman::Sitemap::Resource
47
- Contract None => Maybe[Proc]
48
+ Contract Maybe[Proc]
48
49
  attr_accessor :output
49
50
 
50
51
  def initialize(store, path, target)
@@ -53,7 +54,7 @@ module Middleman
53
54
  super(store, path)
54
55
  end
55
56
 
56
- Contract None => Bool
57
+ Contract Bool
57
58
  def template?
58
59
  true
59
60
  end
@@ -63,7 +64,7 @@ module Middleman
63
64
  url = ::Middleman::Util.url_for(@store.app, @request_path,
64
65
  relative: false,
65
66
  find_resource: true
66
- )
67
+ )
67
68
 
68
69
  if output
69
70
  output.call(path, url)
@@ -82,7 +83,7 @@ module Middleman
82
83
  end
83
84
  end
84
85
 
85
- Contract None => Bool
86
+ Contract Bool
86
87
  def ignored?
87
88
  false
88
89
  end
@@ -4,13 +4,14 @@ module Middleman
4
4
  module Sitemap
5
5
  module Extensions
6
6
  class RequestEndpoints < Extension
7
+ # Expose `create_endpoint` to config as `endpoint`
8
+ expose_to_config endpoint: :create_endpoint
9
+
7
10
  # Manages the list of proxy configurations and manipulates the sitemap
8
11
  # to include new resources based on those configurations
9
12
  def initialize(app, config={}, &block)
10
13
  super
11
14
 
12
- @app.add_to_config_context(:endpoint, &method(:create_endpoint))
13
-
14
15
  @endpoints = {}
15
16
  end
16
17
 
@@ -52,7 +53,7 @@ module Middleman
52
53
  end
53
54
 
54
55
  class EndpointResource < ::Middleman::Sitemap::Resource
55
- Contract None => Maybe[Proc]
56
+ Contract Maybe[Proc]
56
57
  attr_accessor :output
57
58
 
58
59
  def initialize(store, path, request_path)
@@ -60,10 +61,10 @@ module Middleman
60
61
  @request_path = ::Middleman::Util.normalize_path(request_path)
61
62
  end
62
63
 
63
- Contract None => String
64
+ Contract String
64
65
  attr_reader :request_path
65
66
 
66
- Contract None => Bool
67
+ Contract Bool
67
68
  def template?
68
69
  true
69
70
  end
@@ -73,7 +74,7 @@ module Middleman
73
74
  return output.call if output
74
75
  end
75
76
 
76
- Contract None => Bool
77
+ Contract Bool
77
78
  def ignored?
78
79
  false
79
80
  end
@@ -9,7 +9,9 @@ module Middleman
9
9
  tail = parts.pop
10
10
  is_index = (tail == @app.config[:index_file])
11
11
 
12
- return nil if is_index && parts.length < 1
12
+ if parts.empty?
13
+ return is_index ? nil : @store.find_resource_by_path(@app.config[:index_file])
14
+ end
13
15
 
14
16
  test_expr = parts.join('\\/')
15
17
  # eponymous reverse-lookup
@@ -23,7 +23,7 @@ module Middleman
23
23
 
24
24
  # The on-disk source file for this resource, if there is one
25
25
  # @return [String]
26
- Contract None => Maybe[IsA['Middleman::SourceFile']]
26
+ Contract Maybe[IsA['Middleman::SourceFile']]
27
27
  attr_reader :source_file
28
28
 
29
29
  # The path to use when requesting this resource. Normally it's
@@ -35,7 +35,7 @@ module Middleman
35
35
 
36
36
  # The metadata for this resource
37
37
  # @return [Hash]
38
- Contract None => METADATA_HASH
38
+ Contract METADATA_HASH
39
39
  attr_reader :metadata
40
40
 
41
41
  # Initialize resource with parent store and URL
@@ -46,7 +46,7 @@ module Middleman
46
46
  def initialize(store, path, source_file=nil)
47
47
  @store = store
48
48
  @app = @store.app
49
- @path = path.gsub(' ', '%20') # handle spaces in filenames
49
+ @path = path
50
50
 
51
51
  if source_file && source_file.is_a?(String)
52
52
  source_file = Pathname(source_file)
@@ -69,7 +69,7 @@ module Middleman
69
69
 
70
70
  # Whether this resource has a template file
71
71
  # @return [Boolean]
72
- Contract None => Bool
72
+ Contract Bool
73
73
  def template?
74
74
  return false if source_file.nil?
75
75
  !::Tilt[source_file[:full_path].to_s].nil?
@@ -87,31 +87,30 @@ module Middleman
87
87
  end
88
88
 
89
89
  # Data about this resource, populated from frontmatter or extensions.
90
- # @return [HashWithIndifferentAccess]
91
- Contract None => IsA['Middleman::Util::HashWithIndifferentAccess']
90
+ # @return [IndifferentHash]
91
+ Contract IsA['Middleman::Util::IndifferentHash']
92
92
  def data
93
- # TODO: Should this really be a HashWithIndifferentAccess?
94
93
  ::Middleman::Util.recursively_enhance(metadata[:page])
95
94
  end
96
95
 
97
96
  # Options about how this resource is rendered, such as its :layout,
98
97
  # :renderer_options, and whether or not to use :directory_indexes.
99
98
  # @return [Hash]
100
- Contract None => Hash
99
+ Contract Hash
101
100
  def options
102
101
  metadata[:options]
103
102
  end
104
103
 
105
104
  # Local variable mappings that are used when rendering the template for this resource.
106
105
  # @return [Hash]
107
- Contract None => Hash
106
+ Contract Hash
108
107
  def locals
109
108
  metadata[:locals]
110
109
  end
111
110
 
112
111
  # Extension of the path (i.e. '.js')
113
112
  # @return [String]
114
- Contract None => String
113
+ Contract String
115
114
  def ext
116
115
  File.extname(path)
117
116
  end
@@ -141,7 +140,7 @@ module Middleman
141
140
  # A path without the directory index - so foo/index.html becomes
142
141
  # just foo. Best for linking.
143
142
  # @return [String]
144
- Contract None => String
143
+ Contract String
145
144
  def url
146
145
  url_path = destination_path
147
146
  if @app.config[:strip_index_file]
@@ -154,7 +153,7 @@ module Middleman
154
153
  # Whether the source file is binary.
155
154
  #
156
155
  # @return [Boolean]
157
- Contract None => Bool
156
+ Contract Bool
158
157
  def binary?
159
158
  !source_file.nil? && ::Middleman::Util.binary?(source_file[:full_path].to_s)
160
159
  end
@@ -162,14 +161,14 @@ module Middleman
162
161
  # Ignore a resource directly, without going through the whole
163
162
  # ignore filter stuff.
164
163
  # @return [void]
165
- Contract None => Any
164
+ Contract Any
166
165
  def ignore!
167
166
  @ignored = true
168
167
  end
169
168
 
170
169
  # Whether the Resource is ignored
171
170
  # @return [Boolean]
172
- Contract None => Bool
171
+ Contract Bool
173
172
  def ignored?
174
173
  return true if @ignored
175
174
  # Ignore based on the source path (without template extensions)
@@ -184,7 +183,7 @@ module Middleman
184
183
 
185
184
  # The preferred MIME content type for this resource based on extension or metadata
186
185
  # @return [String] MIME type for this resource
187
- Contract None => Maybe[String]
186
+ Contract Maybe[String]
188
187
  def content_type
189
188
  options[:content_type] || ::Rack::Mime.mime_type(ext, nil)
190
189
  end
@@ -74,12 +74,13 @@ module Middleman
74
74
  # @param [Symbol] name Name of the manipulator for debugging
75
75
  # @param [#manipulate_resource_list] manipulator Resource list manipulator
76
76
  # @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.
77
+ # @param [Symbol] custom_name The method name to execute.
77
78
  # @return [void]
78
- Contract Symbol, RespondTo['manipulate_resource_list'], Maybe[Num] => Any
79
- def register_resource_list_manipulator(name, manipulator, priority=50)
79
+ Contract Symbol, RespondTo['manipulate_resource_list'], Maybe[Num], Maybe[Symbol] => Any
80
+ def register_resource_list_manipulator(name, manipulator, priority=50, custom_name=nil)
80
81
  # The third argument used to be a boolean - handle those who still pass one
81
82
  priority = 50 unless priority.is_a? Numeric
82
- @resource_list_manipulators << [name, manipulator, priority]
83
+ @resource_list_manipulators << [name, manipulator, priority, custom_name]
83
84
  # The index trick is used so that the sort is stable - manipulators with the same priority
84
85
  # will always be ordered in the same order as they were registered.
85
86
  n = 0
@@ -177,8 +178,8 @@ module Middleman
177
178
 
178
179
  @app.logger.debug '== Rebuilding resource list'
179
180
 
180
- @resources = @resource_list_manipulators.reduce([]) do |result, (_, manipulator, _)|
181
- newres = manipulator.manipulate_resource_list(result)
181
+ @resources = @resource_list_manipulators.reduce([]) do |result, (_, manipulator, _, custom_name)|
182
+ newres = manipulator.send(custom_name || :manipulate_resource_list, result)
182
183
 
183
184
  # Reset lookup cache
184
185
  reset_lookup_cache!
@@ -17,15 +17,15 @@ module Middleman
17
17
  def_delegator :app, :logger
18
18
 
19
19
  # The type this watcher is representing
20
- Contract None => Symbol
20
+ Contract Symbol
21
21
  attr_reader :type
22
22
 
23
23
  # The directory that is being watched
24
- Contract None => Pathname
24
+ Contract Pathname
25
25
  attr_reader :directory
26
26
 
27
27
  # Options for configuring the watcher
28
- Contract None => Hash
28
+ Contract Hash
29
29
  attr_reader :options
30
30
 
31
31
  # Construct a new SourceWatcher
@@ -47,6 +47,7 @@ module Middleman
47
47
 
48
48
  @validator = options.fetch(:validator, proc { true })
49
49
  @ignored = options.fetch(:ignored, proc { false })
50
+ @only = Array(options.fetch(:only, []))
50
51
 
51
52
  @disable_watcher = app.build? || @parent.options.fetch(:disable_watcher, false)
52
53
  @force_polling = @parent.options.fetch(:force_polling, false)
@@ -79,7 +80,7 @@ module Middleman
79
80
  # Stop watching.
80
81
  #
81
82
  # @return [void]
82
- Contract None => Any
83
+ Contract Any
83
84
  def unwatch
84
85
  stop_listener!
85
86
  end
@@ -87,7 +88,7 @@ module Middleman
87
88
  # All the known files in this watcher.
88
89
  #
89
90
  # @return [Array<Middleman::SourceFile>]
90
- Contract None => ArrayOf[IsA['Middleman::SourceFile']]
91
+ Contract ArrayOf[IsA['Middleman::SourceFile']]
91
92
  def files
92
93
  @files.values
93
94
  end
@@ -123,21 +124,27 @@ module Middleman
123
124
  # Start the `listen` gem Listener.
124
125
  #
125
126
  # @return [void]
126
- Contract None => Any
127
+ Contract Any
127
128
  def listen!
128
129
  return if @disable_watcher || @listener || @waiting_for_existence
129
130
 
130
- config = { force_polling: @force_polling }
131
+ config = {
132
+ force_polling: @force_polling,
133
+ wait_for_delay: 0.5
134
+ }
135
+
131
136
  config[:latency] = @latency if @latency
132
137
 
133
138
  @listener = ::Listen.to(@directory.to_s, config, &method(:on_listener_change))
134
139
  @listener.start
140
+
141
+ @listener.only(@only) unless @only.empty?
135
142
  end
136
143
 
137
144
  # Stop the listener.
138
145
  #
139
146
  # @return [void]
140
- Contract None => Any
147
+ Contract Any
141
148
  def stop_listener!
142
149
  return unless @listener
143
150
 
@@ -148,7 +155,7 @@ module Middleman
148
155
  # Manually trigger update events.
149
156
  #
150
157
  # @return [void]
151
- Contract None => Any
158
+ Contract Any
152
159
  def poll_once!
153
160
  removed = @files.keys
154
161
 
@@ -247,10 +254,9 @@ module Middleman
247
254
  @extensionless_files.delete(strip_extensions(f[:full_path]))
248
255
  end
249
256
 
257
+ Contract Pathname => Pathname
250
258
  def strip_extensions(p)
251
- while ::Tilt[p.to_s] || p.extname === '.html'
252
- p = p.sub_ext('')
253
- end
259
+ p = p.sub_ext('') while ::Tilt[p.to_s] || p.extname == '.html'
254
260
  Pathname(p.to_s + '.*')
255
261
  end
256
262
 
@@ -260,9 +266,13 @@ module Middleman
260
266
  # @return [Boolean]
261
267
  Contract IsA['Middleman::SourceFile'] => Bool
262
268
  def valid?(file)
263
- @validator.call(file) &&
264
- !globally_ignored?(file) &&
269
+ return false unless @validator.call(file) && !globally_ignored?(file)
270
+
271
+ if @only.empty?
265
272
  !@ignored.call(file)
273
+ else
274
+ @only.any? { |reg| reg.match(file[:relative_path].to_s) }
275
+ end
266
276
  end
267
277
 
268
278
  # Convert a path to a file resprentation.
@@ -273,8 +283,11 @@ module Middleman
273
283
  def path_to_source_file(path)
274
284
  types = Set.new([@type])
275
285
 
276
- ::Middleman::SourceFile.new(
277
- path.relative_path_from(@directory), path, @directory, types)
286
+ relative_path = path.relative_path_from(@directory)
287
+ destination_dir = @options.fetch(:destination_dir, false)
288
+ relative_path = File.join(destination_dir, relative_path) if destination_dir
289
+
290
+ ::Middleman::SourceFile.new(Pathname(relative_path), path, @directory, types)
278
291
  end
279
292
 
280
293
  # Notify callbacks for a file given an array of callbacks
@@ -14,15 +14,17 @@ module Middleman
14
14
  extend Forwardable
15
15
  include Contracts
16
16
 
17
+ Matcher = Or[Regexp, RespondTo[:call]]
18
+
17
19
  # A reference to the current app.
18
- Contract None => IsA['Middleman::Application']
20
+ Contract IsA['Middleman::Application']
19
21
  attr_reader :app
20
22
 
21
23
  # Duck-typed definition of a valid source watcher
22
24
  HANDLER = RespondTo[:on_change]
23
25
 
24
26
  # Config
25
- Contract None => Hash
27
+ Contract Hash
26
28
  attr_reader :options
27
29
 
28
30
  # Reference to the global logger.
@@ -125,7 +127,7 @@ module Middleman
125
127
  end
126
128
 
127
129
  # A list of registered watchers
128
- Contract None => ArrayOf[HANDLER]
130
+ Contract ArrayOf[HANDLER]
129
131
  def watchers
130
132
  @sorted_watchers
131
133
  end
@@ -155,7 +157,7 @@ module Middleman
155
157
  # Get all files for this collection of watchers.
156
158
  #
157
159
  # @return [Array<Middleman::SourceFile>]
158
- Contract None => ArrayOf[SourceFile]
160
+ Contract ArrayOf[SourceFile]
159
161
  def files
160
162
  watchers.flat_map(&:files).uniq { |f| f[:relative_path] }
161
163
  end
@@ -204,7 +206,7 @@ module Middleman
204
206
  # Manually poll all watchers for new content.
205
207
  #
206
208
  # @return [void]
207
- Contract None => Any
209
+ Contract Any
208
210
  def find_new_files!
209
211
  return unless @update_count != @last_update_count
210
212
 
@@ -215,7 +217,7 @@ module Middleman
215
217
  # Start up all listeners.
216
218
  #
217
219
  # @return [void]
218
- Contract None => Any
220
+ Contract Any
219
221
  def start!
220
222
  watchers.each(&:listen!)
221
223
  @running = true
@@ -224,7 +226,7 @@ module Middleman
224
226
  # Stop the watchers.
225
227
  #
226
228
  # @return [void]
227
- Contract None => Any
229
+ Contract Any
228
230
  def stop!
229
231
  watchers.each(&:stop_listener!)
230
232
  @running = false
@@ -246,28 +248,24 @@ module Middleman
246
248
  # Backwards compatible change handler.
247
249
  #
248
250
  # @param [nil,Regexp] matcher A Regexp to match the change path against
249
- # Contract Maybe[Regexp] => Any
251
+ Contract Maybe[Matcher] => Any
250
252
  def changed(matcher=nil, &block)
251
253
  on_change :source do |updated, _removed|
252
- updated.select { |f|
253
- matcher.nil? ? true : matches?(matcher, f)
254
- }.each do |f|
255
- block.call(f[:relative_path])
256
- end
254
+ updated
255
+ .select { |f| matcher.nil? ? true : matches?(matcher, f) }
256
+ .each { |f| block.call(f[:relative_path]) }
257
257
  end
258
258
  end
259
259
 
260
260
  # Backwards compatible delete handler.
261
261
  #
262
262
  # @param [nil,Regexp] matcher A Regexp to match the change path against
263
- # Contract Maybe[Regexp] => Any
263
+ Contract Maybe[Matcher] => Any
264
264
  def deleted(matcher=nil, &block)
265
265
  on_change :source do |_updated, removed|
266
- removed.select { |f|
267
- matcher.nil? ? true : matches?(matcher, f)
268
- }.each do |f|
269
- block.call(f[:relative_path])
270
- end
266
+ removed
267
+ .select { |f| matcher.nil? ? true : matches?(matcher, f) }
268
+ .each { |f| block.call(f[:relative_path]) }
271
269
  end
272
270
  end
273
271
 
@@ -287,7 +285,7 @@ module Middleman
287
285
  # @param [Regexp, #call] validator The match validator.
288
286
  # @param [Middleman::SourceFile] file The file to check.
289
287
  # @return [Boolean]
290
- Contract Or[Regexp, RespondTo[:call]], SourceFile => Bool
288
+ Contract Matcher, SourceFile => Bool
291
289
  def matches?(validator, file)
292
290
  path = file[:relative_path]
293
291
  if validator.is_a? Regexp
@@ -300,7 +298,7 @@ module Middleman
300
298
  # Increment the internal counter for changes.
301
299
  #
302
300
  # @return [void]
303
- Contract None => Any
301
+ Contract Any
304
302
  def bump_count
305
303
  @update_count += 1
306
304
  end
@@ -69,7 +69,7 @@ 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
- check_file_content(file, partial_content, true)
72
+ check_file_content(file, Regexp.new(Regexp.escape(partial_content)), true)
73
73
  end
74
74
 
75
75
  And /the file "(.*)" should be gzipped/ do |file|
@@ -70,7 +70,7 @@ end
70
70
 
71
71
  When /^I go to "([^\"]*)"$/ do |url|
72
72
  in_current_dir do
73
- @last_response = @browser.get(URI.escape(url))
73
+ @last_response = @browser.get(URI.encode(url))
74
74
  end
75
75
  end
76
76
 
@@ -78,8 +78,8 @@ Then /^going to "([^\"]*)" should not raise an exception$/ do |url|
78
78
  in_current_dir do
79
79
  last_response = nil
80
80
  expect {
81
- last_response = @browser.get(URI.escape(url))
82
- }.to_not raise_error
81
+ last_response = @browser.get(URI.encode(url))
82
+ }.to_not raise_exception
83
83
  @last_response = last_response
84
84
  end
85
85
  end
@@ -100,7 +100,7 @@ module Middleman
100
100
  def render(_, name, options={}, &block)
101
101
  name = name.to_s
102
102
 
103
- partial_file = locate_partial(name)
103
+ partial_file = locate_partial(name, false) || locate_partial(name, true)
104
104
 
105
105
  return '' unless partial_file
106
106
  raise ::Middleman::TemplateRenderer::TemplateNotFound, "Could not locate partial: #{name}" unless partial_file
@@ -123,8 +123,8 @@ module Middleman
123
123
  # @api private
124
124
  # @param [String] partial_path
125
125
  # @return [String]
126
- Contract String => Maybe[IsA['Middleman::SourceFile']]
127
- def locate_partial(partial_path)
126
+ Contract String, Maybe[Bool] => Maybe[IsA['Middleman::SourceFile']]
127
+ def locate_partial(partial_path, try_static=true)
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
@@ -140,9 +140,9 @@ module Middleman
140
140
  [
141
141
  [relative_dir.to_s, { preferred_engine: resource.source_file[:relative_path].extname[1..-1].to_sym }],
142
142
  [non_root],
143
- [non_root, { try_static: true }],
144
- [relative_dir_no_underscore.to_s, { try_static: true }],
145
- [non_root_no_underscore, { try_static: true }]
143
+ [non_root, { try_static: try_static }],
144
+ [relative_dir_no_underscore.to_s, { try_static: try_static }],
145
+ [non_root_no_underscore, { try_static: try_static }]
146
146
  ].each do |args|
147
147
  partial_file = ::Middleman::TemplateRenderer.resolve_template(@app, *args)
148
148
  break if partial_file
@@ -161,11 +161,12 @@ module Middleman
161
161
  # @param [Hash] opts Template options.
162
162
  # @param [Proc] block A block will be evaluated to return internal contents.
163
163
  # @return [String] The resulting content string.
164
- Contract IsA['Middleman::SourceFile'], Hash, Hash, Proc => String
164
+ Contract IsA['Middleman::SourceFile'], Hash, Hash, Maybe[Proc] => String
165
165
  def render_file(file, locs, opts, &block)
166
166
  _render_with_all_renderers(file[:relative_path].to_s, locs, self, opts, &block)
167
167
  end
168
168
 
169
+ Contract String, Hash, Any, Hash, Maybe[Proc] => String
169
170
  def _render_with_all_renderers(path, locs, context, opts, &block)
170
171
  # Keep rendering template until we've used up all extensions. This
171
172
  # handles cases like `style.css.sass.erb`
@@ -58,8 +58,8 @@ module Middleman
58
58
  # Sandboxed class for template eval
59
59
  context = @app.template_context_class.new(@app, locals, options)
60
60
 
61
- # TODO: Only for HAML files
62
- context.init_haml_helpers if context.respond_to?(:init_haml_helpers)
61
+ # Add extension helpers to context.
62
+ @app.extensions.add_exposed_to_context(context)
63
63
 
64
64
  content = _render_with_all_renderers(path, locs, context, opts, &block)
65
65