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
@@ -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