middleman-core 4.1.0.rc.2 → 4.1.0

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