middleman-core 4.1.0.rc.2 → 4.1.0

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/features/asset_hash.feature +30 -32
  3. data/features/asset_host.feature +2 -0
  4. data/features/gzip.feature +1 -1
  5. data/features/import_files.feature +0 -2
  6. data/features/nested_layouts.feature +20 -17
  7. data/fixtures/asset-host-app/source/javascripts/asset_host.js +2 -0
  8. data/fixtures/frontmatter-neighbor-app/config.rb +1 -1
  9. data/fixtures/frontmatter-settings-neighbor-app/config.rb +1 -1
  10. data/fixtures/nested-layout-app/source/layouts/inner.erb +5 -2
  11. data/fixtures/nested-layout-app/source/layouts/inner_haml.haml +6 -2
  12. data/fixtures/nested-layout-app/source/layouts/inner_slim.slim +6 -2
  13. data/fixtures/nested-layout-app/source/layouts/master.erb +7 -1
  14. data/fixtures/nested-layout-app/source/layouts/master_haml.haml +5 -1
  15. data/fixtures/nested-layout-app/source/layouts/master_slim.slim +5 -1
  16. data/fixtures/nested-layout-app/source/layouts/outer.erb +6 -2
  17. data/fixtures/nested-layout-app/source/layouts/outer_haml.haml +5 -1
  18. data/fixtures/nested-layout-app/source/layouts/outer_slim.slim +5 -1
  19. data/lib/middleman-core.rb +0 -3
  20. data/lib/middleman-core/application.rb +7 -9
  21. data/lib/middleman-core/builder.rb +88 -44
  22. data/lib/middleman-core/contracts.rb +102 -13
  23. data/lib/middleman-core/core_extensions/data.rb +15 -10
  24. data/lib/middleman-core/core_extensions/default_helpers.rb +15 -6
  25. data/lib/middleman-core/core_extensions/file_watcher.rb +2 -2
  26. data/lib/middleman-core/core_extensions/front_matter.rb +11 -3
  27. data/lib/middleman-core/core_extensions/i18n.rb +1 -1
  28. data/lib/middleman-core/core_extensions/inline_url_rewriter.rb +2 -2
  29. data/lib/middleman-core/extension.rb +1 -1
  30. data/lib/middleman-core/extensions.rb +1 -1
  31. data/lib/middleman-core/extensions/asset_hash.rb +1 -1
  32. data/lib/middleman-core/extensions/asset_host.rb +1 -1
  33. data/lib/middleman-core/extensions/automatic_image_sizes.rb +1 -1
  34. data/lib/middleman-core/extensions/cache_buster.rb +1 -1
  35. data/lib/middleman-core/extensions/external_pipeline.rb +2 -1
  36. data/lib/middleman-core/extensions/gzip.rb +2 -2
  37. data/lib/middleman-core/extensions/minify_css.rb +1 -1
  38. data/lib/middleman-core/extensions/minify_javascript.rb +1 -1
  39. data/lib/middleman-core/extensions/relative_assets.rb +1 -1
  40. data/lib/middleman-core/file_renderer.rb +12 -9
  41. data/lib/middleman-core/logger.rb +1 -0
  42. data/lib/middleman-core/preview_server.rb +14 -14
  43. data/lib/middleman-core/renderers/haml.rb +3 -1
  44. data/lib/middleman-core/renderers/less.rb +1 -1
  45. data/lib/middleman-core/renderers/liquid.rb +1 -1
  46. data/lib/middleman-core/renderers/sass.rb +7 -2
  47. data/lib/middleman-core/sitemap/extensions/ignores.rb +2 -2
  48. data/lib/middleman-core/sitemap/extensions/import.rb +3 -1
  49. data/lib/middleman-core/sitemap/resource.rb +7 -6
  50. data/lib/middleman-core/sources.rb +30 -13
  51. data/lib/middleman-core/sources/source_watcher.rb +50 -12
  52. data/lib/middleman-core/step_definitions/middleman_steps.rb +2 -2
  53. data/lib/middleman-core/template_context.rb +1 -1
  54. data/lib/middleman-core/template_renderer.rb +13 -4
  55. data/lib/middleman-core/util.rb +6 -606
  56. data/lib/middleman-core/util/binary.rb +79 -0
  57. data/lib/middleman-core/util/data.rb +37 -8
  58. data/lib/middleman-core/util/files.rb +134 -0
  59. data/lib/middleman-core/util/paths.rb +251 -0
  60. data/lib/middleman-core/util/rack.rb +52 -0
  61. data/lib/middleman-core/util/uri_templates.rb +97 -0
  62. data/lib/middleman-core/version.rb +1 -1
  63. data/middleman-core.gemspec +1 -0
  64. metadata +25 -4
@@ -1,23 +1,112 @@
1
- require 'contracts'
2
- require 'hamster'
1
+ if ENV['CONTRACTS'] != 'false'
2
+ require 'contracts'
3
+ require 'hamster'
3
4
 
4
- module Contracts
5
- class IsA
6
- def self.[](val)
7
- @lookup ||= {}
8
- @lookup[val] ||= new(val)
5
+ module Contracts
6
+ class IsA
7
+ def self.[](val)
8
+ @lookup ||= {}
9
+ @lookup[val] ||= new(val)
10
+ end
11
+
12
+ def initialize(val)
13
+ @val = val
14
+ end
15
+
16
+ def valid?(val)
17
+ val.is_a? @val.constantize
18
+ end
19
+ end
20
+
21
+ VectorOf = Contracts::CollectionOf::Factory.new(::Hamster::Vector)
22
+ ResourceList = Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']]
23
+ end
24
+ else
25
+ module Contracts
26
+ def self.included(base)
27
+ base.extend self
28
+ end
29
+
30
+ # rubocop:disable MethodName
31
+ def Contract(*)
32
+ end
33
+
34
+ class Callable
35
+ def self.[](*)
36
+ end
37
+ end
38
+
39
+ class Bool
40
+ end
41
+
42
+ class Num
43
+ end
44
+
45
+ class Pos
46
+ end
47
+
48
+ class Neg
49
+ end
50
+
51
+ class Any
52
+ end
53
+
54
+ class None
55
+ end
56
+
57
+ class Or < Callable
58
+ end
59
+
60
+ class Xor < Callable
61
+ end
62
+
63
+ class And < Callable
64
+ end
65
+
66
+ class Not < Callable
67
+ end
68
+
69
+ class RespondTo < Callable
70
+ end
71
+
72
+ class Send < Callable
9
73
  end
10
74
 
11
- def initialize(val)
12
- @val = val
75
+ class Exactly < Callable
13
76
  end
14
77
 
15
- def valid?(val)
16
- val.is_a? @val.constantize
78
+ class ArrayOf < Callable
79
+ end
80
+
81
+ class ResourceList < Callable
82
+ end
83
+
84
+ class Args < Callable
85
+ end
86
+
87
+ class HashOf < Callable
88
+ end
89
+
90
+ class Bool
91
+ end
92
+
93
+ class Maybe < Callable
94
+ end
95
+
96
+ class IsA < Callable
97
+ end
98
+
99
+ class SetOf < Callable
100
+ end
101
+
102
+ class Frozen < Callable
103
+ end
104
+
105
+ class VectorOf < Callable
17
106
  end
18
107
  end
108
+ end
19
109
 
20
- VectorOf = ::Contracts::CollectionOf::Factory.new(::Hamster::Vector)
21
- ResourceList = ::Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']]
110
+ module Contracts
22
111
  PATH_MATCHER = Or[String, RespondTo[:match], RespondTo[:call], RespondTo[:to_s]]
23
112
  end
@@ -8,7 +8,7 @@ module Middleman
8
8
  class Data < Extension
9
9
  attr_reader :data_store
10
10
 
11
- define_setting :data_dir, 'data', 'The directory data files are stored in'
11
+ define_setting :data_dir, ENV['MM_DATA_DIR'] || 'data', 'The directory data files are stored in'
12
12
 
13
13
  # Make the internal `data_store` method available as `app.data`
14
14
  expose_to_application data: :data_store
@@ -57,6 +57,7 @@ module Middleman
57
57
  @app = app
58
58
  @data_file_matcher = data_file_matcher
59
59
  @local_data = {}
60
+ @local_data_enhanced = nil
60
61
  @local_sources = {}
61
62
  @callback_sources = {}
62
63
  end
@@ -99,13 +100,13 @@ module Middleman
99
100
  extension = File.extname(data_path)
100
101
  basename = File.basename(data_path, extension)
101
102
 
103
+ return unless %w(.yaml .yml .json).include?(extension)
104
+
102
105
  if %w(.yaml .yml).include?(extension)
103
- data, postscript = ::Middleman::Util::Data.parse(file[:full_path], @app.config[:frontmatter_delims], :yaml)
106
+ data, postscript = ::Middleman::Util::Data.parse(file, @app.config[:frontmatter_delims], :yaml)
104
107
  data[:postscript] = postscript if !postscript.nil? && data.is_a?(Hash)
105
108
  elsif extension == '.json'
106
- data, _postscript = ::Middleman::Util::Data.parse(file[:full_path], @app.config[:frontmatter_delims], :json)
107
- else
108
- return
109
+ data, _postscript = ::Middleman::Util::Data.parse(file, @app.config[:frontmatter_delims], :json)
109
110
  end
110
111
 
111
112
  data_branch = @local_data
@@ -117,6 +118,8 @@ module Middleman
117
118
  end
118
119
 
119
120
  data_branch[basename] = data
121
+
122
+ @local_data_enhanced = nil
120
123
  end
121
124
 
122
125
  # Remove a given file from the internal cache
@@ -137,6 +140,8 @@ module Middleman
137
140
  end
138
141
 
139
142
  data_branch.delete(basename) if data_branch.key?(basename)
143
+
144
+ @local_data_enhanced = nil
140
145
  end
141
146
 
142
147
  # Get a hash from either internal static data or a callback
@@ -151,8 +156,7 @@ module Middleman
151
156
  callbacks[path.to_s].call
152
157
  end
153
158
 
154
- response = ::Middleman::Util.recursively_enhance(response)
155
- response
159
+ ::Middleman::Util.recursively_enhance(response)
156
160
  end
157
161
 
158
162
  # "Magically" find namespaces of data if they exist
@@ -162,7 +166,8 @@ module Middleman
162
166
  def method_missing(path)
163
167
  if @local_data.key?(path.to_s)
164
168
  # Any way to cache this?
165
- return ::Middleman::Util.recursively_enhance(@local_data[path.to_s])
169
+ @local_data_enhanced ||= ::Middleman::Util.recursively_enhance(@local_data)
170
+ return @local_data_enhanced[path.to_s]
166
171
  else
167
172
  result = data_for_path(path)
168
173
  return result if result
@@ -198,11 +203,11 @@ module Middleman
198
203
  def to_h
199
204
  data = {}
200
205
 
201
- store.each do |k, _|
206
+ store.each_key do |k|
202
207
  data[k] = data_for_path(k)
203
208
  end
204
209
 
205
- callbacks.each do |k, _|
210
+ callbacks.each_key do |k|
206
211
  data[k] = data_for_path(k)
207
212
  end
208
213
 
@@ -113,7 +113,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
113
113
  path_options = {}
114
114
  path_options[:relative] = options.delete(:relative) if options.key?(:relative)
115
115
 
116
- sources.flatten.inject(::ActiveSupport::SafeBuffer.new) do |all, source|
116
+ sources.flatten.reduce(::ActiveSupport::SafeBuffer.new) do |all, source|
117
117
  all << tag(:link, {
118
118
  href: asset_path(:css, source, path_options)
119
119
  }.update(options))
@@ -127,7 +127,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
127
127
  path_options = {}
128
128
  path_options[:relative] = options.delete(:relative) if options.key?(:relative)
129
129
 
130
- sources.flatten.inject(::ActiveSupport::SafeBuffer.new) do |all, source|
130
+ sources.flatten.reduce(::ActiveSupport::SafeBuffer.new) do |all, source|
131
131
  all << content_tag(:script, nil, {
132
132
  src: asset_path(:js, source, path_options)
133
133
  }.update(options))
@@ -152,7 +152,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
152
152
  # If the basename of the request as no extension, assume we are serving a
153
153
  # directory and join index_file to the path.
154
154
  path = File.join(asset_dir, current_resource.path)
155
- path = path.sub(/#{Regexp.escape(File.extname(path))}$/, ".#{asset_ext}")
155
+ path = path[0..-(File.extname(path).length + 1)] + ".#{asset_ext}"
156
156
 
157
157
  yield path if sitemap.find_resource_by_path(path)
158
158
  end
@@ -191,7 +191,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
191
191
  # @param [Hash] options Data to pass through.
192
192
  # @return [String]
193
193
  def asset_path(kind, source, options={})
194
- options_with_resource = options.merge(current_resource: current_resource)
194
+ options_with_resource = {}.merge!(options).merge!(current_resource: current_resource)
195
195
  ::Middleman::Util.asset_path(app, kind, source, options_with_resource)
196
196
  end
197
197
 
@@ -202,7 +202,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
202
202
  # @param [Hash] options Additional options.
203
203
  # @return [String] The fully qualified asset url
204
204
  def asset_url(path, prefix='', options={})
205
- options_with_resource = options.merge(current_resource: current_resource)
205
+ options_with_resource = {}.merge!(options).merge!(current_resource: current_resource)
206
206
  ::Middleman::Util.asset_url(app, path, prefix, options_with_resource)
207
207
  end
208
208
 
@@ -210,7 +210,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
210
210
  # or a Resource, this will produce the nice URL configured for that
211
211
  # path, respecting :relative_links, directory indexes, etc.
212
212
  def url_for(path_or_resource, options={})
213
- options_with_resource = options.merge(current_resource: current_resource)
213
+ options_with_resource = {}.merge!(options).merge!(current_resource: current_resource)
214
214
  ::Middleman::Util.url_for(app, path_or_resource, options_with_resource)
215
215
  end
216
216
 
@@ -276,5 +276,14 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
276
276
 
277
277
  super(path, params)
278
278
  end
279
+
280
+ def partial(template, options={}, &block)
281
+ including_parent_locals = {}
282
+ including_parent_locals.merge!(@locs || {})
283
+ including_parent_locals.merge!(options[:locals] || {})
284
+
285
+ options[:locals] = including_parent_locals
286
+ super(template, options, &block)
287
+ end
279
288
  end
280
289
  end
@@ -47,7 +47,7 @@ module Middleman
47
47
  # @return [void]
48
48
  Contract Any
49
49
  def before_configuration
50
- @sources.find_new_files!
50
+ @sources.poll_once!
51
51
  end
52
52
 
53
53
  # After we config, find new files since config can change paths.
@@ -60,7 +60,7 @@ module Middleman
60
60
  end
61
61
 
62
62
  @sources.start!
63
- @sources.find_new_files!
63
+ @sources.poll_once!
64
64
  end
65
65
 
66
66
  protected
@@ -14,6 +14,12 @@ module Middleman::CoreExtensions
14
14
  # Try to run after routing but before directory_indexes
15
15
  self.resource_list_manipulator_priority = 20
16
16
 
17
+ # Set textual delimiters that denote the start and end of frontmatter
18
+ define_setting :frontmatter_delims, {
19
+ json: [%w(;;; ;;;)],
20
+ yaml: [%w(--- ---), %w(--- ...)]
21
+ }, 'Allowed frontmatter delimiters'
22
+
17
23
  def initialize(app, options_hash={}, &block)
18
24
  super
19
25
 
@@ -29,8 +35,8 @@ module Middleman::CoreExtensions
29
35
  def manipulate_resource_list(resources)
30
36
  resources.each do |resource|
31
37
  next if resource.binary?
32
- next if resource.ignored?
33
38
  next if resource.file_descriptor.nil?
39
+ next if resource.file_descriptor[:types].include?(:no_frontmatter)
34
40
 
35
41
  fmdata = data(resource.file_descriptor[:full_path].to_s).first.dup
36
42
 
@@ -67,8 +73,10 @@ module Middleman::CoreExtensions
67
73
 
68
74
  return [{}, nil] unless file
69
75
 
70
- @cache[file[:full_path]] ||= ::Middleman::Util::Data.parse(
71
- file[:full_path],
76
+ return @cache[file[:full_path]] if @cache.key?(file[:full_path])
77
+
78
+ @cache[file[:full_path]] = ::Middleman::Util::Data.parse(
79
+ file,
72
80
  app.config[:frontmatter_delims]
73
81
  )
74
82
  end
@@ -203,7 +203,7 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
203
203
  if (options[:mount_at_root] == locale) || (options[:mount_at_root].nil? && locales[0] == locale)
204
204
  '/'
205
205
  else
206
- replacement = options[:locale_map].fetch(locale, locale)
206
+ replacement = options[:locale_map][locale] || locale
207
207
  options[:path].sub(':locale', replacement.to_s).sub(':lang', replacement.to_s) # Backward compat
208
208
  end
209
209
  end
@@ -108,7 +108,7 @@ module Middleman
108
108
  ignore = rewriter.fetch(:ignore)
109
109
  next if ignore.any? { |r| should_ignore?(r, full_asset_path) }
110
110
 
111
- rewrite_ignore = Array(rewriter.fetch(:rewrite_ignore, []))
111
+ rewrite_ignore = Array(rewriter[:rewrite_ignore] || [])
112
112
  next if rewrite_ignore.any? { |i| ::Middleman::Util.path_match(i, path) }
113
113
 
114
114
  proc = rewriter.fetch(:proc)
@@ -132,7 +132,7 @@ module Middleman
132
132
  def should_ignore?(validator, value)
133
133
  if validator.is_a? Regexp
134
134
  # Treat as Regexp
135
- !value.match(validator).nil?
135
+ !!(value =~ validator)
136
136
  elsif validator.respond_to? :call
137
137
  # Treat as proc
138
138
  validator.call(value)
@@ -432,7 +432,7 @@ module Middleman
432
432
  {}
433
433
  end
434
434
 
435
- sum.merge(resource_definitions)
435
+ sum.merge!(resource_definitions)
436
436
  end
437
437
 
438
438
  resources + generator_defs.map do |path, g|
@@ -106,7 +106,7 @@ module Middleman
106
106
  # A flattened list of all extensions which are automatically activated
107
107
  # @return [Array<Symbol>] A list of extension names which are automatically activated.
108
108
  def auto_activated
109
- @auto_activate.values.map(&:to_a).flatten.map(&:name)
109
+ @auto_activate.values.map(&:to_a).flat_map(&:name)
110
110
  end
111
111
 
112
112
  # @api private
@@ -2,7 +2,7 @@ require 'middleman-core/util'
2
2
  require 'middleman-core/rack'
3
3
 
4
4
  class Middleman::Extensions::AssetHash < ::Middleman::Extension
5
- option :sources, %w(.htm .html .php .css .js), 'List of extensions that are searched for hashable assets.'
5
+ option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for hashable assets.'
6
6
  option :exts, %w(.jpg .jpeg .png .gif .webp .js .css .otf .woff .woff2 .eot .ttf .svg .svgz), 'List of extensions that get asset hashes appended to them.'
7
7
  option :ignore, [], 'Regexes of filenames to skip adding asset hashes to'
8
8
  option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites'
@@ -3,7 +3,7 @@ require 'addressable/uri'
3
3
  class Middleman::Extensions::AssetHost < ::Middleman::Extension
4
4
  option :host, nil, 'The asset host to use or a Proc to determine asset host', required: true
5
5
  option :exts, %w(.css .png .jpg .jpeg .webp .svg .svgz .js .gif), 'List of extensions that get cache busters strings appended to them.'
6
- option :sources, %w(.htm .html .php .css .js), 'List of extensions that are searched for bustable assets.'
6
+ option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for bustable assets.'
7
7
  option :ignore, [], 'Regexes of filenames to skip adding query strings to'
8
8
  option :rewrite_ignore, [], 'Regexes of filenames to skip processing for host rewrites'
9
9
 
@@ -19,7 +19,7 @@ class Middleman::Extensions::AutomaticImageSizes < ::Middleman::Extension
19
19
  real_path = path.dup
20
20
  real_path = File.join(config[:images_dir], real_path) unless real_path.start_with?('/')
21
21
 
22
- file = app.files.find(:source, real_path) || app.files.find(:source, real_path.gsub(/^\//, ''))
22
+ file = app.files.find(:source, real_path) || app.files.find(:source, real_path.sub(/^\//, ''))
23
23
 
24
24
  if file && file[:full_path].exist?
25
25
  begin
@@ -1,7 +1,7 @@
1
1
  # The Cache Buster extension
2
2
  class Middleman::Extensions::CacheBuster < ::Middleman::Extension
3
3
  option :exts, %w(.css .png .jpg .jpeg .webp .svg .svgz .js .gif), 'List of extensions that get cache busters strings appended to them.'
4
- option :sources, %w(.htm .html .php .css .js), 'List of extensions that are searched for bustable assets.'
4
+ option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for bustable assets.'
5
5
  option :ignore, [], 'Regexes of filenames to skip adding query strings to'
6
6
  option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites'
7
7
 
@@ -14,7 +14,8 @@ class Middleman::Extensions::ExternalPipeline < ::Middleman::Extension
14
14
 
15
15
  @watcher = app.files.watch :source,
16
16
  path: File.expand_path(options[:source], app.root),
17
- latency: options[:latency]
17
+ latency: options[:latency],
18
+ frontmatter: false
18
19
 
19
20
  logger.info "== Executing: `#{options[:command]}`"
20
21
 
@@ -7,10 +7,10 @@
7
7
  # to serve your Gzipped files whenever the normal (non-.gz) filename is requested.
8
8
  #
9
9
  # Pass the :exts options to customize which file extensions get zipped (defaults
10
- # to .html, .htm, .js and .css.
10
+ # to .css, .htm, .html, .js, and .xhtml
11
11
  #
12
12
  class Middleman::Extensions::Gzip < ::Middleman::Extension
13
- option :exts, %w(.js .css .html .htm .svg), 'File extensions to Gzip when building.'
13
+ option :exts, %w(.css .htm .html .js .svg .xhtml), 'File extensions to Gzip when building.'
14
14
  option :ignore, [], 'Patterns to avoid gzipping'
15
15
  option :overwrite, false, 'Overwrite original files instead of adding .gz extension.'
16
16
 
@@ -23,7 +23,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
23
23
  class SassCompressor
24
24
  def self.compress(style, options={})
25
25
  root_node = ::Sass::SCSS::CssParser.new(style, 'middleman-css-input', 1).parse
26
- root_node.options = options.merge(style: :compressed)
26
+ root_node.options = {}.merge!(options).merge!(style: :compressed)
27
27
  root_node.render.strip
28
28
  end
29
29
  end