middleman-core 4.0.0.alpha.2 → 4.0.0.alpha.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/features/asset_hash.feature +26 -8
- data/features/chained_templates.feature +88 -1
- data/features/cli_init.feature +0 -18
- data/features/collections.feature +145 -0
- data/features/console.feature +11 -0
- data/features/front-matter.feature +11 -0
- data/features/paginate.feature +204 -0
- data/features/partials.feature +0 -5
- data/features/slim.feature +1 -1
- data/features/working_directory.feature +33 -0
- data/fixtures/asset-hash-app/source/api.json.erb +1 -0
- data/fixtures/asset-hash-app/source/images/200px.jpg +0 -0
- data/fixtures/asset-hash-app/source/images/300px.jpg +0 -0
- data/fixtures/asset-hash-app/source/index.html.erb +1 -1
- data/fixtures/asset-hash-app/source/subdir/api.json.erb +1 -0
- data/fixtures/collections-app/config.rb +16 -0
- data/fixtures/collections-app/source/blog1/2011-01-01-new-article.html.markdown +7 -0
- data/fixtures/collections-app/source/blog1/2011-01-02-another-article.html.markdown +9 -0
- data/fixtures/collections-app/source/blog2/2011-01-01-new-article.html.markdown +7 -0
- data/fixtures/collections-app/source/blog2/2011-01-02-another-article.html.markdown +8 -0
- data/fixtures/collections-app/source/index.html.erb +26 -0
- data/fixtures/frontmatter-app/source/front-matter-pandoc.html.md.erb +13 -0
- data/fixtures/generator-test/config.rb +2 -7
- data/fixtures/multiple-data-sources-app/source/index.html.erb +0 -5
- data/fixtures/{more-instance-vars-app → paginate-app}/config.rb +0 -0
- data/fixtures/paginate-app/source/archive/2011/index.html.erb +20 -0
- data/fixtures/paginate-app/source/blog/2011-01-01-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/blog/2011-01-02-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/blog/2011-01-03-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/blog/2011-01-04-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/blog/2011-01-05-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/blog/2011-02-01-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/blog/2011-02-02-test-article.html.markdown +6 -0
- data/fixtures/paginate-app/source/index.html.erb +15 -0
- data/fixtures/paginate-app/source/tag.html.erb +23 -0
- data/fixtures/partial-chained_templates-app/config.rb +0 -0
- data/lib/middleman-core/application.rb +28 -1
- data/lib/middleman-core/builder.rb +2 -0
- data/lib/middleman-core/contracts.rb +19 -1
- data/lib/middleman-core/core_extensions.rb +5 -0
- data/lib/middleman-core/core_extensions/collections.rb +82 -0
- data/lib/middleman-core/core_extensions/collections/lazy_root.rb +30 -0
- data/lib/middleman-core/core_extensions/collections/lazy_step.rb +48 -0
- data/lib/middleman-core/core_extensions/collections/pagination.rb +59 -0
- data/lib/middleman-core/core_extensions/collections/step_context.rb +26 -0
- data/lib/middleman-core/core_extensions/data.rb +1 -1
- data/lib/middleman-core/core_extensions/default_helpers.rb +1 -2
- data/lib/middleman-core/core_extensions/front_matter.rb +10 -3
- data/lib/middleman-core/core_extensions/i18n.rb +6 -7
- data/lib/middleman-core/core_extensions/show_exceptions.rb +1 -1
- data/lib/middleman-core/extension.rb +1 -1
- data/lib/middleman-core/extensions/automatic_image_sizes.rb +0 -2
- data/lib/middleman-core/file_renderer.rb +3 -1
- data/lib/middleman-core/load_paths.rb +2 -1
- data/lib/middleman-core/logger.rb +1 -1
- data/lib/middleman-core/meta_pages/sitemap_resource.rb +1 -1
- data/lib/middleman-core/preview_server.rb +1 -1
- data/lib/middleman-core/renderers/sass.rb +1 -1
- data/lib/middleman-core/renderers/slim.rb +2 -2
- data/lib/middleman-core/sitemap/extensions/on_disk.rb +1 -1
- data/lib/middleman-core/sitemap/extensions/proxies.rb +36 -50
- data/lib/middleman-core/sitemap/resource.rb +42 -3
- data/lib/middleman-core/sitemap/store.rb +5 -0
- data/lib/middleman-core/sources.rb +64 -24
- data/lib/middleman-core/sources/source_watcher.rb +47 -23
- data/lib/middleman-core/step_definitions/server_steps.rb +52 -21
- data/lib/middleman-core/template_context.rb +26 -5
- data/lib/middleman-core/template_renderer.rb +50 -33
- data/lib/middleman-core/util.rb +94 -1
- data/lib/middleman-core/util/hash_with_indifferent_access.rb +1 -1
- data/lib/middleman-core/version.rb +1 -1
- data/middleman-core.gemspec +2 -0
- metadata +90 -15
- data/features/more-instance_vars.feature +0 -18
- data/fixtures/more-instance-vars-app/source/_vartial.erb +0 -5
- data/fixtures/more-instance-vars-app/source/instance-var-set.html.erb +0 -2
- data/fixtures/more-instance-vars-app/source/layout.erb +0 -3
- data/fixtures/more-instance-vars-app/source/no-instance-var.html.erb +0 -1
@@ -24,7 +24,7 @@ module Middleman
|
|
24
24
|
super
|
25
25
|
|
26
26
|
# Setup Slim options to work with partials
|
27
|
-
::Slim::Engine.
|
27
|
+
::Slim::Engine.set_options(
|
28
28
|
buffer: '@_out_buf',
|
29
29
|
use_html_safe: true,
|
30
30
|
generator: ::Temple::Generators::RailsOutputBuffer,
|
@@ -39,7 +39,7 @@ module Middleman
|
|
39
39
|
|
40
40
|
::Slim::Embedded::SassEngine.disable_option_validator!
|
41
41
|
%w(sass scss markdown).each do |engine|
|
42
|
-
::Slim::Embedded.
|
42
|
+
::Slim::Embedded.options[engine.to_sym] = context_hack
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'middleman-core/sitemap/resource'
|
2
|
+
require 'middleman-core/core_extensions/collections/step_context'
|
2
3
|
|
3
4
|
module Middleman
|
4
5
|
module Sitemap
|
@@ -13,6 +14,13 @@ module Middleman
|
|
13
14
|
@app.define_singleton_method(:proxy, &method(:create_proxy))
|
14
15
|
|
15
16
|
@proxy_configs = Set.new
|
17
|
+
@post_config = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def after_configuration
|
21
|
+
@post_config = true
|
22
|
+
|
23
|
+
::Middleman::CoreExtensions::Collections::StepContext.add_to_context(:proxy, &method(:create_anonymous_proxy))
|
16
24
|
end
|
17
25
|
|
18
26
|
# Setup a proxy from a path to a target
|
@@ -27,70 +35,48 @@ module Middleman
|
|
27
35
|
Contract String, String, Maybe[Hash] => Any
|
28
36
|
def create_proxy(path, target, opts={})
|
29
37
|
options = opts.dup
|
30
|
-
|
31
38
|
@app.ignore(target) if options.delete(:ignore)
|
32
39
|
|
33
|
-
|
34
|
-
options: options,
|
35
|
-
locals: options.delete(:locals) || {},
|
36
|
-
page: options.delete(:data) || {}
|
37
|
-
}
|
38
|
-
|
39
|
-
@proxy_configs << ProxyConfiguration.new(path: path, target: target, metadata: metadata)
|
40
|
-
|
40
|
+
@proxy_configs << create_anonymous_proxy(path, target, options)
|
41
41
|
@app.sitemap.rebuild_resource_list!(:added_proxy)
|
42
42
|
end
|
43
43
|
|
44
|
+
# Setup a proxy from a path to a target
|
45
|
+
# @param [String] path The new, proxied path to create
|
46
|
+
# @param [String] target The existing path that should be proxied to. This must be a real resource, not another proxy.
|
47
|
+
# @option opts [Boolean] ignore Ignore the target from the sitemap (so only the new, proxy resource ends up in the output)
|
48
|
+
# @option opts [Symbol, Boolean, String] layout The layout name to use (e.g. `:article`) or `false` to disable layout.
|
49
|
+
# @option opts [Boolean] directory_indexes Whether or not the `:directory_indexes` extension applies to these paths.
|
50
|
+
# @option opts [Hash] locals Local variables for the template. These will be available when the template renders.
|
51
|
+
# @option opts [Hash] data Extra metadata to add to the page. This is the same as frontmatter, though frontmatter will take precedence over metadata defined here. Available via {Resource#data}.
|
52
|
+
# @return [void]
|
53
|
+
def create_anonymous_proxy(path, target, options={})
|
54
|
+
ProxyDescriptor.new(
|
55
|
+
::Middleman::Util.normalize_path(path),
|
56
|
+
::Middleman::Util.normalize_path(target),
|
57
|
+
options
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
44
61
|
# Update the main sitemap resource list
|
45
62
|
# @return Array<Middleman::Sitemap::Resource>
|
46
63
|
Contract ResourceList => ResourceList
|
47
64
|
def manipulate_resource_list(resources)
|
48
|
-
resources + @proxy_configs.map
|
49
|
-
p = ProxyResource.new(
|
50
|
-
@app.sitemap,
|
51
|
-
config.path,
|
52
|
-
config.target
|
53
|
-
)
|
54
|
-
|
55
|
-
p.add_metadata(config.metadata)
|
56
|
-
p
|
57
|
-
end
|
65
|
+
resources + @proxy_configs.map { |c| c.to_resource(@app) }
|
58
66
|
end
|
59
67
|
end
|
60
68
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
attr_reader :target
|
71
|
-
def target=(t)
|
72
|
-
@target = ::Middleman::Util.normalize_path(t)
|
73
|
-
end
|
74
|
-
|
75
|
-
# Additional metadata like locals to apply to the proxy
|
76
|
-
attr_accessor :metadata
|
77
|
-
|
78
|
-
# Create a new proxy configuration from hash options
|
79
|
-
def initialize(options={})
|
80
|
-
options.each do |key, value|
|
81
|
-
send "#{key}=", value
|
69
|
+
ProxyDescriptor = Struct.new(:path, :target, :metadata) do
|
70
|
+
def to_resource(app)
|
71
|
+
ProxyResource.new(app.sitemap, path, target).tap do |p|
|
72
|
+
md = metadata.dup
|
73
|
+
p.add_metadata(
|
74
|
+
locals: md.delete(:locals) || {},
|
75
|
+
page: md.delete(:data) || {},
|
76
|
+
options: md
|
77
|
+
)
|
82
78
|
end
|
83
79
|
end
|
84
|
-
|
85
|
-
# Two configurations are equal if they reference the same path
|
86
|
-
def eql?(other)
|
87
|
-
other.path == path
|
88
|
-
end
|
89
|
-
|
90
|
-
# Two configurations are equal if they reference the same path
|
91
|
-
def hash
|
92
|
-
path.hash
|
93
|
-
end
|
94
80
|
end
|
95
81
|
end
|
96
82
|
|
@@ -42,12 +42,22 @@ module Middleman
|
|
42
42
|
# @param [Middleman::Sitemap::Store] store
|
43
43
|
# @param [String] path
|
44
44
|
# @param [String] source_file
|
45
|
-
Contract IsA['Middleman::Sitemap::Store'], String, Maybe[IsA['Middleman::SourceFile']] => Any
|
45
|
+
Contract IsA['Middleman::Sitemap::Store'], String, Maybe[Or[IsA['Middleman::SourceFile'], String]] => Any
|
46
46
|
def initialize(store, path, source_file=nil)
|
47
47
|
@store = store
|
48
48
|
@app = @store.app
|
49
49
|
@path = path.gsub(' ', '%20') # handle spaces in filenames
|
50
|
-
|
50
|
+
|
51
|
+
if source_file && source_file.is_a?(String)
|
52
|
+
source_file = Pathname(source_file)
|
53
|
+
end
|
54
|
+
|
55
|
+
if source_file && source_file.is_a?(Pathname)
|
56
|
+
@source_file = ::Middleman::SourceFile.new(source_file.relative_path_from(@app.source_dir), source_file, @app.source_dir, Set.new([:source]))
|
57
|
+
else
|
58
|
+
@source_file = source_file
|
59
|
+
end
|
60
|
+
|
51
61
|
@destination_path = @path
|
52
62
|
|
53
63
|
# Options are generally rendering/sitemap options
|
@@ -165,7 +175,11 @@ module Middleman
|
|
165
175
|
# Ignore based on the source path (without template extensions)
|
166
176
|
return true if @app.sitemap.ignored?(path)
|
167
177
|
# This allows files to be ignored by their source file name (with template extensions)
|
168
|
-
!self.is_a?(ProxyResource) && @app.sitemap.ignored?(source_file[:relative_path].to_s)
|
178
|
+
if !self.is_a?(ProxyResource) && source_file && @app.sitemap.ignored?(source_file[:relative_path].to_s)
|
179
|
+
true
|
180
|
+
else
|
181
|
+
false
|
182
|
+
end
|
169
183
|
end
|
170
184
|
|
171
185
|
# The preferred MIME content type for this resource based on extension or metadata
|
@@ -174,6 +188,31 @@ module Middleman
|
|
174
188
|
def content_type
|
175
189
|
options[:content_type] || ::Rack::Mime.mime_type(ext, nil)
|
176
190
|
end
|
191
|
+
|
192
|
+
def to_s
|
193
|
+
"#<Middleman::Sitemap::Resource path=#{@path}>"
|
194
|
+
end
|
195
|
+
alias_method :inspect, :to_s # Ruby 2.0 calls inspect for NoMethodError instead of to_s
|
196
|
+
end
|
197
|
+
|
198
|
+
class StringResource < Resource
|
199
|
+
def initialize(store, path, contents=nil, &block)
|
200
|
+
@request_path = path
|
201
|
+
@contents = block_given? ? block : contents
|
202
|
+
super(store, path)
|
203
|
+
end
|
204
|
+
|
205
|
+
def template?
|
206
|
+
true
|
207
|
+
end
|
208
|
+
|
209
|
+
def render(*)
|
210
|
+
@contents.respond_to?(:call) ? @contents.call : @contents
|
211
|
+
end
|
212
|
+
|
213
|
+
def binary?
|
214
|
+
false
|
215
|
+
end
|
177
216
|
end
|
178
217
|
end
|
179
218
|
end
|
@@ -49,11 +49,15 @@ module Middleman
|
|
49
49
|
# @return [Middleman::Application]
|
50
50
|
attr_reader :app
|
51
51
|
|
52
|
+
attr_reader :update_count
|
53
|
+
|
52
54
|
# Initialize with parent app
|
53
55
|
# @param [Middleman::Application] app
|
54
56
|
def initialize(app)
|
55
57
|
@app = app
|
56
58
|
@resources = []
|
59
|
+
@update_count = 0
|
60
|
+
|
57
61
|
# TODO: Should this be a set or hash?
|
58
62
|
@resource_list_manipulators = []
|
59
63
|
@needs_sitemap_rebuild = true
|
@@ -187,6 +191,7 @@ module Middleman
|
|
187
191
|
end
|
188
192
|
|
189
193
|
invalidate_resources_not_ignored_cache!
|
194
|
+
@update_count += 1
|
190
195
|
end
|
191
196
|
end
|
192
197
|
|
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'middleman-core/contracts'
|
2
|
+
require 'backports/2.0.0/enumerable/lazy'
|
2
3
|
|
3
4
|
module Middleman
|
4
5
|
# The standard "record" that contains information about a file on disk.
|
5
|
-
SourceFile = Struct.new :relative_path, :full_path, :directory, :
|
6
|
+
SourceFile = Struct.new :relative_path, :full_path, :directory, :types
|
6
7
|
|
7
8
|
# Sources handle multiple on-disk collections of files which make up
|
8
9
|
# a Middleman project. They are separated by `type` which can then be
|
@@ -18,7 +19,7 @@ module Middleman
|
|
18
19
|
attr_reader :app
|
19
20
|
|
20
21
|
# Duck-typed definition of a valid source watcher
|
21
|
-
HANDLER = RespondTo[:
|
22
|
+
HANDLER = RespondTo[:on_change]
|
22
23
|
|
23
24
|
# Config
|
24
25
|
Contract None => Hash
|
@@ -82,8 +83,8 @@ module Middleman
|
|
82
83
|
Contract SourceFile => Bool
|
83
84
|
def globally_ignored?(file)
|
84
85
|
@ignores.values.any? do |descriptor|
|
85
|
-
((descriptor[:type] == :all) ||
|
86
|
-
|
86
|
+
((descriptor[:type] == :all) || file[:types].include?(descriptor[:type])) &&
|
87
|
+
matches?(descriptor[:validator], file)
|
87
88
|
end
|
88
89
|
end
|
89
90
|
|
@@ -113,7 +114,7 @@ module Middleman
|
|
113
114
|
[priority, n]
|
114
115
|
end.reverse.freeze
|
115
116
|
|
116
|
-
handler.
|
117
|
+
handler.on_change(&method(:did_change))
|
117
118
|
|
118
119
|
if @running
|
119
120
|
handler.poll_once!
|
@@ -133,7 +134,7 @@ module Middleman
|
|
133
134
|
#
|
134
135
|
# @param [SourceWatcher] watcher The watcher to remove.
|
135
136
|
# @return [void]
|
136
|
-
Contract RespondTo[:
|
137
|
+
Contract RespondTo[:on_change] => Any
|
137
138
|
def unwatch(watcher)
|
138
139
|
@watchers.delete(watcher)
|
139
140
|
|
@@ -156,7 +157,7 @@ module Middleman
|
|
156
157
|
# @return [Array<Middleman::SourceFile>]
|
157
158
|
Contract None => ArrayOf[SourceFile]
|
158
159
|
def files
|
159
|
-
watchers.
|
160
|
+
watchers.flat_map(&:files).uniq { |f| f[:relative_path] }
|
160
161
|
end
|
161
162
|
|
162
163
|
# Find a file given a type and path.
|
@@ -168,10 +169,11 @@ module Middleman
|
|
168
169
|
Contract Symbol, String, Maybe[Bool] => Maybe[SourceFile]
|
169
170
|
def find(type, path, glob=false)
|
170
171
|
watchers
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
172
|
+
.lazy
|
173
|
+
.select { |d| d.type == type }
|
174
|
+
.map { |d| d.find(path, glob) }
|
175
|
+
.reject(&:nil?)
|
176
|
+
.first
|
175
177
|
end
|
176
178
|
|
177
179
|
# Check if a file for a given type exists.
|
@@ -182,8 +184,9 @@ module Middleman
|
|
182
184
|
Contract Symbol, String => Bool
|
183
185
|
def exists?(type, path)
|
184
186
|
watchers
|
185
|
-
|
186
|
-
|
187
|
+
.lazy
|
188
|
+
.select { |d| d.type == type }
|
189
|
+
.any? { |d| d.exists?(path) }
|
187
190
|
end
|
188
191
|
|
189
192
|
# Check if a file for a given type exists.
|
@@ -191,11 +194,11 @@ module Middleman
|
|
191
194
|
# @param [Symbol] type The file "type".
|
192
195
|
# @param [String] path The file path relative to it's source root.
|
193
196
|
# @return [Boolean]
|
194
|
-
Contract Symbol, String => Maybe[HANDLER]
|
195
|
-
def watcher_for_path(
|
197
|
+
Contract SetOf[Symbol], String => Maybe[HANDLER]
|
198
|
+
def watcher_for_path(types, path)
|
196
199
|
watchers
|
197
|
-
|
198
|
-
|
200
|
+
.select { |d| types.include?(d.type) }
|
201
|
+
.find { |d| d.exists?(path) }
|
199
202
|
end
|
200
203
|
|
201
204
|
# Manually poll all watchers for new content.
|
@@ -230,16 +233,53 @@ module Middleman
|
|
230
233
|
# A callback requires a type and the proc to execute.
|
231
234
|
CallbackDescriptor = Struct.new :type, :proc
|
232
235
|
|
233
|
-
# Add callback to be run on file change
|
236
|
+
# Add callback to be run on file change or deletion
|
234
237
|
#
|
235
|
-
# @param [
|
238
|
+
# @param [Symbol] type The change type.
|
236
239
|
# @return [Set<CallbackDescriptor>]
|
237
240
|
Contract Symbol, Proc => ArrayOf[CallbackDescriptor]
|
238
|
-
def
|
241
|
+
def on_change(type, &block)
|
239
242
|
@on_change_callbacks << CallbackDescriptor.new(type, block)
|
240
243
|
@on_change_callbacks
|
241
244
|
end
|
242
245
|
|
246
|
+
# Backwards compatible change handler.
|
247
|
+
#
|
248
|
+
# @param [nil,Regexp] matcher A Regexp to match the change path against
|
249
|
+
# Contract Maybe[Regexp] => Any
|
250
|
+
def changed(matcher=nil, &block)
|
251
|
+
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
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Backwards compatible delete handler.
|
261
|
+
#
|
262
|
+
# @param [nil,Regexp] matcher A Regexp to match the change path against
|
263
|
+
# Contract Maybe[Regexp] => Any
|
264
|
+
def deleted(matcher=nil, &block)
|
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
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# Backwards compatible ignored check.
|
275
|
+
#
|
276
|
+
# @param [Pathname,String] path The path to check.
|
277
|
+
Contract Or[Pathname, String] => Bool
|
278
|
+
def ignored?(path)
|
279
|
+
descriptor = find(:source, path)
|
280
|
+
!descriptor || globally_ignored?(descriptor)
|
281
|
+
end
|
282
|
+
|
243
283
|
protected
|
244
284
|
|
245
285
|
# Whether a validator matches a file.
|
@@ -272,11 +312,11 @@ module Middleman
|
|
272
312
|
Contract ArrayOf[SourceFile], ArrayOf[SourceFile], HANDLER => Any
|
273
313
|
def did_change(updated_files, removed_files, watcher)
|
274
314
|
valid_updated = updated_files.select do |file|
|
275
|
-
watcher_for_path(file[:
|
315
|
+
watcher_for_path(file[:types], file[:relative_path].to_s) == watcher
|
276
316
|
end
|
277
317
|
|
278
318
|
valid_removed = removed_files.select do |file|
|
279
|
-
watcher_for_path(file[:
|
319
|
+
watcher_for_path(file[:types], file[:relative_path].to_s).nil?
|
280
320
|
end
|
281
321
|
|
282
322
|
return if valid_updated.empty? && valid_removed.empty?
|
@@ -296,8 +336,8 @@ module Middleman
|
|
296
336
|
if callback[:type] == :all
|
297
337
|
callback[:proc].call(updated_files, removed_files)
|
298
338
|
else
|
299
|
-
valid_updated = updated_files.select { |f|
|
300
|
-
valid_removed = removed_files.select { |f|
|
339
|
+
valid_updated = updated_files.select { |f| f[:types].include?(callback[:type]) }
|
340
|
+
valid_removed = removed_files.select { |f| f[:types].include?(callback[:type]) }
|
301
341
|
|
302
342
|
callback[:proc].call(valid_updated, valid_removed)
|
303
343
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Watcher Library
|
2
2
|
require 'listen'
|
3
|
-
|
4
3
|
require 'middleman-core/contracts'
|
4
|
+
require 'backports/2.0.0/enumerable/lazy'
|
5
5
|
|
6
6
|
module Middleman
|
7
7
|
# The default source watcher implementation. Watches a directory on disk
|
@@ -43,6 +43,7 @@ module Middleman
|
|
43
43
|
@directory = Pathname(directory)
|
44
44
|
|
45
45
|
@files = {}
|
46
|
+
@extensionless_files = {}
|
46
47
|
|
47
48
|
@validator = options.fetch(:validator, proc { true })
|
48
49
|
@ignored = options.fetch(:ignored, proc { false })
|
@@ -103,10 +104,8 @@ module Middleman
|
|
103
104
|
return nil if p.absolute? && !p.to_s.start_with?(@directory.to_s)
|
104
105
|
|
105
106
|
p = @directory + p if p.relative?
|
106
|
-
|
107
107
|
if glob
|
108
|
-
|
109
|
-
found ? found.last : nil
|
108
|
+
@extensionless_files[p]
|
110
109
|
else
|
111
110
|
@files[p]
|
112
111
|
end
|
@@ -173,7 +172,7 @@ module Middleman
|
|
173
172
|
# @param [Proc] matcher A Regexp to match the change path against
|
174
173
|
# @return [Set<Proc>]
|
175
174
|
Contract Proc => SetOf[Proc]
|
176
|
-
def
|
175
|
+
def on_change(&block)
|
177
176
|
@on_change_callbacks << block
|
178
177
|
@on_change_callbacks
|
179
178
|
end
|
@@ -211,25 +210,48 @@ module Middleman
|
|
211
210
|
Contract ArrayOf[Pathname], ArrayOf[Pathname] => Any
|
212
211
|
def update(updated_paths, removed_paths)
|
213
212
|
valid_updates = updated_paths
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
213
|
+
.lazy
|
214
|
+
.map(&method(:path_to_source_file))
|
215
|
+
.select(&method(:valid?))
|
216
|
+
.to_a
|
217
|
+
.each do |f|
|
218
|
+
add_file_to_cache(f)
|
219
|
+
logger.debug "== Change (#{f[:types].inspect}): #{f[:relative_path]}"
|
220
|
+
end
|
221
221
|
|
222
222
|
valid_removes = removed_paths
|
223
|
-
|
224
|
-
|
225
|
-
|
223
|
+
.lazy
|
224
|
+
.select(&@files.method(:key?))
|
225
|
+
.map(&@files.method(:[]))
|
226
|
+
.select(&method(:valid?))
|
227
|
+
.to_a
|
228
|
+
.each do |f|
|
229
|
+
remove_file_from_cache(f)
|
230
|
+
logger.debug "== Deletion (#{f[:types].inspect}): #{f[:relative_path]}"
|
231
|
+
end
|
232
|
+
|
233
|
+
run_callbacks(
|
234
|
+
@on_change_callbacks,
|
235
|
+
valid_updates,
|
236
|
+
valid_removes
|
237
|
+
) unless valid_updates.empty? && valid_removes.empty?
|
238
|
+
end
|
226
239
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
240
|
+
def add_file_to_cache(f)
|
241
|
+
@files[f[:full_path]] = f
|
242
|
+
@extensionless_files[strip_extensions(f[:full_path])] = f
|
243
|
+
end
|
231
244
|
|
232
|
-
|
245
|
+
def remove_file_from_cache(f)
|
246
|
+
@files.delete(f[:full_path])
|
247
|
+
@extensionless_files.delete(strip_extensions(f[:full_path]))
|
248
|
+
end
|
249
|
+
|
250
|
+
def strip_extensions(p)
|
251
|
+
while ::Tilt[p.to_s] || p.extname === '.html'
|
252
|
+
p = p.sub_ext('')
|
253
|
+
end
|
254
|
+
Pathname(p.to_s + '.*')
|
233
255
|
end
|
234
256
|
|
235
257
|
# Check if this watcher should care about a file.
|
@@ -239,8 +261,8 @@ module Middleman
|
|
239
261
|
Contract IsA['Middleman::SourceFile'] => Bool
|
240
262
|
def valid?(file)
|
241
263
|
@validator.call(file) &&
|
242
|
-
|
243
|
-
|
264
|
+
!globally_ignored?(file) &&
|
265
|
+
!@ignored.call(file)
|
244
266
|
end
|
245
267
|
|
246
268
|
# Convert a path to a file resprentation.
|
@@ -249,8 +271,10 @@ module Middleman
|
|
249
271
|
# @return [Middleman::SourceFile]
|
250
272
|
Contract Pathname => IsA['Middleman::SourceFile']
|
251
273
|
def path_to_source_file(path)
|
274
|
+
types = Set.new([@type])
|
275
|
+
|
252
276
|
::Middleman::SourceFile.new(
|
253
|
-
|
277
|
+
path.relative_path_from(@directory), path, @directory, types)
|
254
278
|
end
|
255
279
|
|
256
280
|
# Notify callbacks for a file given an array of callbacks
|