middleman-core 4.0.0.beta.2 → 4.0.0.rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/features/builder.feature +9 -1
  3. data/features/collections.feature +43 -2
  4. data/features/data.feature +7 -0
  5. data/features/extension_hooks.feature +13 -0
  6. data/features/front-matter-neighbor.feature +0 -11
  7. data/features/front-matter.feature +0 -22
  8. data/features/markdown_kramdown_in_slim.feature +42 -0
  9. data/fixtures/extension-hooks-app/config.rb +39 -0
  10. data/fixtures/extension-hooks-app/source/index.html.erb +9 -0
  11. data/fixtures/frontmatter-app/source/front-matter-line-2.html.erb +4 -1
  12. data/fixtures/frontmatter-neighbor-app/config.rb +3 -3
  13. data/fixtures/frontmatter-settings-neighbor-app/config.rb +4 -4
  14. data/fixtures/markdown-in-slim-app/config.rb +0 -0
  15. data/fixtures/markdown-in-slim-app/source/images/blank.gif +0 -0
  16. data/fixtures/markdown-in-slim-app/source/link_target.html.markdown +4 -0
  17. data/fixtures/more-traversal-app/source/layout.erb +1 -1
  18. data/fixtures/nested-data-app/data/examples/withcontent.yaml +11 -0
  19. data/fixtures/nested-data-app/source/extracontent.html.haml.erb +4 -0
  20. data/fixtures/traversal-app/source/layout.erb +1 -1
  21. data/lib/middleman-core/application.rb +7 -3
  22. data/lib/middleman-core/builder.rb +2 -1
  23. data/lib/middleman-core/configuration.rb +0 -1
  24. data/lib/middleman-core/contracts.rb +0 -10
  25. data/lib/middleman-core/core_extensions/collections.rb +30 -6
  26. data/lib/middleman-core/core_extensions/data.rb +4 -4
  27. data/lib/middleman-core/core_extensions/file_watcher.rb +1 -1
  28. data/lib/middleman-core/core_extensions/front_matter.rb +10 -104
  29. data/lib/middleman-core/core_extensions/show_exceptions.rb +1 -1
  30. data/lib/middleman-core/extension_manager.rb +2 -1
  31. data/lib/middleman-core/extensions/asset_hash.rb +2 -1
  32. data/lib/middleman-core/extensions/asset_host.rb +1 -1
  33. data/lib/middleman-core/extensions/external_pipeline.rb +4 -1
  34. data/lib/middleman-core/extensions/minify_css.rb +1 -1
  35. data/lib/middleman-core/extensions/minify_javascript.rb +1 -1
  36. data/lib/middleman-core/extensions/relative_assets.rb +1 -1
  37. data/lib/middleman-core/extensions.rb +1 -1
  38. data/lib/middleman-core/file_renderer.rb +6 -4
  39. data/lib/middleman-core/meta_pages/sitemap_resource.rb +1 -1
  40. data/lib/middleman-core/meta_pages/templates/config.html.erb +14 -14
  41. data/lib/middleman-core/meta_pages.rb +6 -1
  42. data/lib/middleman-core/preview_server.rb +21 -2
  43. data/lib/middleman-core/rack.rb +5 -3
  44. data/lib/middleman-core/renderers/haml.rb +22 -6
  45. data/lib/middleman-core/renderers/kramdown.rb +10 -3
  46. data/lib/middleman-core/renderers/liquid.rb +22 -3
  47. data/lib/middleman-core/renderers/redcarpet.rb +7 -5
  48. data/lib/middleman-core/renderers/sass.rb +1 -5
  49. data/lib/middleman-core/renderers/slim.rb +16 -12
  50. data/lib/middleman-core/sitemap/extensions/ignores.rb +2 -0
  51. data/lib/middleman-core/sitemap/extensions/on_disk.rb +2 -0
  52. data/lib/middleman-core/sitemap/extensions/proxies.rb +18 -3
  53. data/lib/middleman-core/sitemap/extensions/redirects.rb +2 -0
  54. data/lib/middleman-core/sitemap/extensions/request_endpoints.rb +2 -0
  55. data/lib/middleman-core/sitemap/resource.rb +24 -17
  56. data/lib/middleman-core/sitemap/store.rb +1 -0
  57. data/lib/middleman-core/sources/source_watcher.rb +26 -1
  58. data/lib/middleman-core/step_definitions/builder_steps.rb +8 -8
  59. data/lib/middleman-core/step_definitions/middleman_steps.rb +8 -2
  60. data/lib/middleman-core/step_definitions/server_steps.rb +17 -18
  61. data/lib/middleman-core/step_definitions.rb +0 -1
  62. data/lib/middleman-core/template_context.rb +2 -2
  63. data/lib/middleman-core/util/data.rb +153 -0
  64. data/lib/middleman-core/util.rb +14 -45
  65. data/lib/middleman-core/version.rb +1 -1
  66. data/middleman-core.gemspec +3 -2
  67. data/spec/middleman-core/util_spec.rb +3 -23
  68. metadata +37 -26
  69. data/fixtures/frontmatter-app/source/json-front-matter-2.php.erb +0 -7
  70. data/fixtures/frontmatter-app/source/json-front-matter-encoding.html.erb +0 -7
  71. data/fixtures/frontmatter-app/source/json-front-matter-line-2.html.erb +0 -7
  72. data/fixtures/frontmatter-app/source/json-front-matter.html.erb +0 -6
  73. data/fixtures/frontmatter-neighbor-app/source/json-front-matter-2.php.erb +0 -2
  74. data/fixtures/frontmatter-neighbor-app/source/json-front-matter-2.php.erb.frontmatter +0 -4
  75. data/fixtures/frontmatter-neighbor-app/source/json-front-matter.html.erb +0 -1
  76. data/fixtures/frontmatter-neighbor-app/source/json-front-matter.html.erb.frontmatter +0 -4
@@ -1,24 +1,18 @@
1
- require 'active_support/core_ext/hash/keys'
1
+ # Core Pathname library used for traversal
2
2
  require 'pathname'
3
3
 
4
- # Parsing YAML frontmatter
5
- require 'yaml'
4
+ # DbC
5
+ require 'middleman-core/contracts'
6
+
7
+ require 'active_support/core_ext/hash/keys'
6
8
 
7
- # Parsing JSON frontmatter
8
- require 'active_support/json'
9
+ require 'middleman-core/util/data'
9
10
 
10
11
  # Extensions namespace
11
12
  module Middleman::CoreExtensions
12
13
  class FrontMatter < ::Middleman::Extension
13
14
  # Try to run after routing but before directory_indexes
14
- self.resource_list_manipulator_priority = 90
15
-
16
- YAML_ERRORS = [StandardError]
17
-
18
- # https://github.com/tenderlove/psych/issues/23
19
- if defined?(Psych) && defined?(Psych::SyntaxError)
20
- YAML_ERRORS << Psych::SyntaxError
21
- end
15
+ self.resource_list_manipulator_priority = 10
22
16
 
23
17
  def initialize(app, options_hash={}, &block)
24
18
  super
@@ -34,9 +28,9 @@ module Middleman::CoreExtensions
34
28
  Contract ResourceList => ResourceList
35
29
  def manipulate_resource_list(resources)
36
30
  resources.each do |resource|
37
- next if resource.source_file.nil?
31
+ next if resource.file_descriptor.nil?
38
32
 
39
- fmdata = data(resource.source_file[:full_path].to_s).first.dup
33
+ fmdata = data(resource.file_descriptor[:full_path].to_s).first.dup
40
34
 
41
35
  # Copy over special options
42
36
  # TODO: Should we make people put these under "options" instead of having
@@ -71,7 +65,7 @@ module Middleman::CoreExtensions
71
65
 
72
66
  return [{}, nil] unless file
73
67
 
74
- @cache[file[:full_path]] ||= frontmatter_and_content(file[:full_path])
68
+ @cache[file[:full_path]] ||= ::Middleman::Util::Data.parse(file[:full_path])
75
69
  end
76
70
 
77
71
  Contract ArrayOf[IsA['Middleman::SourceFile']], ArrayOf[IsA['Middleman::SourceFile']] => Any
@@ -80,93 +74,5 @@ module Middleman::CoreExtensions
80
74
  @cache.delete(file[:full_path])
81
75
  end
82
76
  end
83
-
84
- # Get the frontmatter and plain content from a file
85
- # @param [String] path
86
- # @return [Array<Middleman::Util::IndifferentHash, String>]
87
- Contract Pathname => [Hash, Maybe[String]]
88
- def frontmatter_and_content(full_path)
89
- data = {}
90
-
91
- return [data, nil] if ::Middleman::Util.binary?(full_path)
92
-
93
- # Avoid weird race condition when a file is renamed.
94
- content = begin
95
- File.read(full_path)
96
- rescue ::EOFError
97
- rescue ::IOError
98
- rescue ::Errno::ENOENT
99
- ''
100
- end
101
-
102
- begin
103
- if content =~ /\A.*coding:/
104
- lines = content.split(/\n/)
105
- lines.shift
106
- content = lines.join("\n")
107
- end
108
-
109
- result = parse_yaml_front_matter(content, full_path) || parse_json_front_matter(content, full_path)
110
- return result if result
111
- rescue
112
- # Probably a binary file, move on
113
- end
114
-
115
- [data, content]
116
- end
117
-
118
- private
119
-
120
- # Parse YAML frontmatter out of a string
121
- # @param [String] content
122
- # @return [Array<Hash, String>]
123
- Contract String, Pathname => Maybe[[Hash, String]]
124
- def parse_yaml_front_matter(content, full_path)
125
- yaml_regex = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
126
- if content =~ yaml_regex
127
- content = content.sub(yaml_regex, '')
128
-
129
- begin
130
- data = YAML.load($1) || {}
131
- data = data.symbolize_keys
132
- rescue *YAML_ERRORS => e
133
- app.logger.error "YAML Exception parsing #{full_path}: #{e.message}"
134
- return nil
135
- end
136
- else
137
- return nil
138
- end
139
-
140
- [data, content]
141
- rescue
142
- [{}, content]
143
- end
144
-
145
- # Parse JSON frontmatter out of a string
146
- # @param [String] content
147
- # @return [Array<Hash, String>]
148
- Contract String, Pathname => Maybe[[Hash, String]]
149
- def parse_json_front_matter(content, full_path)
150
- json_regex = /\A(;;;\s*\n.*?\n?)^(;;;\s*$\n?)/m
151
-
152
- if content =~ json_regex
153
- content = content.sub(json_regex, '')
154
-
155
- begin
156
- json = ($1 + $2).sub(';;;', '{').sub(';;;', '}')
157
- data = ::ActiveSupport::JSON.decode(json).symbolize_keys
158
- rescue => e
159
- app.logger.error "JSON Exception parsing #{full_path}: #{e.message}"
160
- return nil
161
- end
162
-
163
- else
164
- return nil
165
- end
166
-
167
- [data, content]
168
- rescue
169
- [{}, content]
170
- end
171
77
  end
172
78
  end
@@ -11,7 +11,7 @@ module Middleman::CoreExtensions
11
11
  app.config.define_setting :show_exceptions, ENV['TEST'] ? false : true, 'Whether to catch and display exceptions'
12
12
  end
13
13
 
14
- def after_configuration
14
+ def ready
15
15
  app.use ::Rack::ShowExceptions if !app.build? && app.config[:show_exceptions]
16
16
  end
17
17
  end
@@ -3,13 +3,14 @@ module Middleman
3
3
  extend Forwardable
4
4
 
5
5
  def_delegator :@app, :logger
6
- def_delegators :@activated, :[]
6
+ def_delegators :@activated, :[], :each
7
7
 
8
8
  def initialize(app)
9
9
  @app = app
10
10
  @activated = {}
11
11
 
12
12
  manager = self
13
+
13
14
  {
14
15
  before_sitemap: :before_sitemap,
15
16
  initialized: :before_configuration
@@ -1,5 +1,6 @@
1
1
  require 'addressable/uri'
2
2
  require 'middleman-core/util'
3
+ require 'middleman-core/rack'
3
4
 
4
5
  class Middleman::Extensions::AssetHash < ::Middleman::Extension
5
6
  option :exts, %w(.jpg .jpeg .png .gif .webp .js .css .otf .woff .woff2 .eot .ttf .svg), 'List of extensions that get asset hashes appended to them.'
@@ -16,7 +17,7 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
16
17
  def after_configuration
17
18
  # Allow specifying regexes to ignore, plus always ignore apple touch icons
18
19
  @ignore = Array(options.ignore) + [/^apple-touch-icon/]
19
-
20
+
20
21
  app.use ::Middleman::Middleware::InlineURLRewriter,
21
22
  id: :asset_hash,
22
23
  url_extensions: options.exts,
@@ -7,7 +7,7 @@ class Middleman::Extensions::AssetHost < ::Middleman::Extension
7
7
  option :sources, %w(.htm .html .php .css .js), 'List of extensions that are searched for bustable assets.'
8
8
  option :ignore, [], 'Regexes of filenames to skip adding query strings to'
9
9
 
10
- def after_configuration
10
+ def ready
11
11
  app.use ::Middleman::Middleware::InlineURLRewriter,
12
12
  id: :asset_host,
13
13
  url_extensions: options.exts,
@@ -16,7 +16,7 @@ class Middleman::Extensions::ExternalPipeline < ::Middleman::Extension
16
16
  latency: options[:latency]
17
17
  end
18
18
 
19
- def after_configuration
19
+ def ready
20
20
  if app.build?
21
21
  logger.info "== Executing: `#{options[:command]}`"
22
22
  watch_command!
@@ -33,5 +33,8 @@ class Middleman::Extensions::ExternalPipeline < ::Middleman::Extension
33
33
  logger.info "== External: #{without_newline}" if without_newline.length > 0
34
34
  end
35
35
  end
36
+ rescue ::Errno::ENOENT => e
37
+ logger.error "== External: Command failed with message: #{e.message}"
38
+ exit(1)
36
39
  end
37
40
  end
@@ -9,7 +9,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
9
9
  SassCompressor
10
10
  }, 'Set the CSS compressor to use.'
11
11
 
12
- def after_configuration
12
+ def ready
13
13
  # Setup Rack middleware to minify CSS
14
14
  app.use Rack, compressor: options[:compressor],
15
15
  ignore: Array(options[:ignore]) + [/\.min\./],
@@ -9,7 +9,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
9
9
  ::Uglifier.new
10
10
  }, 'Set the JS compressor to use.'
11
11
 
12
- def after_configuration
12
+ def ready
13
13
  # Setup Rack middleware to minify CSS
14
14
  app.use Rack, compressor: options[:compressor],
15
15
  ignore: Array(options[:ignore]) + [/\.min\./],
@@ -12,7 +12,7 @@ class Middleman::Extensions::RelativeAssets < ::Middleman::Extension
12
12
  require 'middleman-core/middleware/inline_url_rewriter'
13
13
  end
14
14
 
15
- def after_configuration
15
+ def ready
16
16
  app.use ::Middleman::Middleware::InlineURLRewriter,
17
17
  id: :asset_hash,
18
18
  url_extensions: options.exts,
@@ -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.flat_map(&:name)
109
+ @auto_activate.values.map(&:to_a).flatten.map(&:name)
110
110
  end
111
111
 
112
112
  # @api private
@@ -53,6 +53,7 @@ module Middleman
53
53
  extension = File.extname(path)
54
54
  options = opts.merge(options_for_ext(extension))
55
55
  options[:outvar] ||= '@_out_buf'
56
+ options[:context] = context
56
57
  options.delete(:layout)
57
58
 
58
59
  # Overwrite with frontmatter options
@@ -66,9 +67,10 @@ module Middleman
66
67
  end
67
68
 
68
69
  # Read compiled template from disk or cache
69
- template = cache.fetch(:compiled_template, extension, options, body) do
70
- ::Tilt.new(path, 1, options) { body }
71
- end
70
+ template = ::Tilt.new(path, 1, options) { body }
71
+ # template = cache.fetch(:compiled_template, extension, options, body) do
72
+ # ::Tilt.new(path, 1, options) { body }
73
+ # end
72
74
 
73
75
  # Render using Tilt
74
76
  content = ::Middleman::Util.instrument 'render.tilt', path: path do
@@ -76,7 +78,7 @@ module Middleman
76
78
  end
77
79
 
78
80
  # Allow hooks to manipulate the result after render
79
- content = @app.callbacks_for(:before_render).reduce(content) do |sum, callback|
81
+ content = @app.callbacks_for(:after_render).reduce(content) do |sum, callback|
80
82
  callback.call(sum, path, locs, template_class) || sum
81
83
  end
82
84
 
@@ -39,7 +39,7 @@ module Middleman
39
39
  build_path = 'Not built' if ignored?
40
40
  props['Build Path'] = build_path if @resource.path != build_path
41
41
  props['URL'] = content_tag(:a, @resource.url, href: @resource.url) unless ignored?
42
- props['Source File'] = @resource.source_file ? @resource.source_file[:full_path].to_s.sub(/^#{Regexp.escape(ENV['MM_ROOT'] + '/')}/, '') : 'Dynamic'
42
+ props['Source File'] = @resource.file_descriptor ? @resource.file_descriptor[:full_path].to_s.sub(/^#{Regexp.escape(ENV['MM_ROOT'] + '/')}/, '') : 'Dynamic'
43
43
 
44
44
  data = @resource.data
45
45
  props['Data'] = data.inspect unless data.empty?
@@ -29,22 +29,22 @@
29
29
 
30
30
  <h2 id="extensions">Extensions</h2>
31
31
  <ul class="extensions">
32
- <% extension_config.each do |ext_name, configs| %>
33
- <li>
34
- <% registered_extensions.delete(ext_name) %>
35
-
36
- <span class="extension active">:<%= ext_name %></span>
37
-
38
-
39
- <% if configs && !configs.empty? %>
40
- <ul class="settings">
41
- <% configs.each do |setting| %>
42
- <li class="setting"><%= setting.render %></li>
32
+ <% [extension_config, auto_activated_config].each do |extensions| %>
33
+ <% extensions.each do |ext_name, configs| %>
34
+ <li>
35
+ <% registered_extensions.delete(ext_name) %>
36
+
37
+ <span class="extension active">:<%= ext_name %></span>
38
+
39
+ <% if configs && !configs.empty? %>
40
+ <ul class="settings">
41
+ <% configs.each do |setting| %>
42
+ <li class="setting"><%= setting.render %></li>
43
+ <% end %>
44
+ </ul>
43
45
  <% end %>
44
- </ul>
46
+ </li>
45
47
  <% end %>
46
- </li>
47
-
48
48
  <% end %>
49
49
 
50
50
  <% registered_extensions.keys.each do |ext_name| %>
@@ -61,9 +61,13 @@ module Middleman
61
61
  def config(_)
62
62
  global_config = @middleman.config.all_settings.map { |c| ConfigSetting.new(c) }
63
63
  extension_config = {}
64
+ auto_activated_config = {}
64
65
 
65
66
  @middleman.extensions.each do |ext_name, extension|
66
- next if ::Middleman::Extension.auto_activated.include? ext_name
67
+ if ::Middleman::Extensions.auto_activated.include? ext_name
68
+ auto_activated_config[ext_name] = extension_options(extension)
69
+ next
70
+ end
67
71
 
68
72
  if extension.is_a?(Hash)
69
73
  # Multiple instance extension
@@ -82,6 +86,7 @@ module Middleman
82
86
  template('config.html.erb',
83
87
  global_config: global_config,
84
88
  extension_config: extension_config,
89
+ auto_activated_config: auto_activated_config,
85
90
  registered_extensions: Middleman::Extensions.registered.dup)
86
91
  end
87
92
 
@@ -1,6 +1,7 @@
1
1
  require 'webrick'
2
2
  require 'webrick/https'
3
3
  require 'openssl'
4
+ require 'socket'
4
5
  require 'middleman-core/meta_pages'
5
6
  require 'middleman-core/logger'
6
7
  require 'middleman-core/rack'
@@ -123,9 +124,18 @@ module Middleman
123
124
  ]
124
125
 
125
126
  # config.rb
126
- files.watch :reload,
127
+ watcher = files.watch :reload,
127
128
  path: root,
128
129
  only: match_against
130
+
131
+ # Hack around node_modules in root.
132
+ watcher.listener.ignore(/^node_modules/)
133
+
134
+ # Hack around sass cache in root.
135
+ watcher.listener.ignore(/^\.sass-cache/)
136
+
137
+ # Hack around bundler cache in root.
138
+ watcher.listener.ignore(/^vendor\/bundle/)
129
139
  end
130
140
  end
131
141
 
@@ -200,7 +210,7 @@ module Middleman
200
210
  begin
201
211
  ::WEBrick::HTTPServer.new(http_opts)
202
212
  rescue Errno::EADDRINUSE
203
- logger.error "== Port #{port} is unavailable. Either close the instance of Middleman already running on #{port} or start this Middleman on a new port with: --port=#{port.to_i + 1}"
213
+ logger.error "== Port #{port} is unavailable. Either close the instance of Middleman already running on #{port} or start this Middleman on a new port with: --port=#{unused_tcp_port}"
204
214
  exit(1)
205
215
  end
206
216
  end
@@ -234,6 +244,15 @@ module Middleman
234
244
  scheme = https? ? 'https' : 'http'
235
245
  URI("#{scheme}://#{host}:#{@port}")
236
246
  end
247
+
248
+ # Returns unused TCP port
249
+ # @return [Fixnum]
250
+ def unused_tcp_port
251
+ server = TCPServer.open(0)
252
+ port = server.addr[1]
253
+ server.close
254
+ port
255
+ end
237
256
  end
238
257
 
239
258
  class FilteredWebrickLog < ::WEBrick::Log
@@ -4,6 +4,7 @@ require 'rack/lint'
4
4
  require 'rack/head'
5
5
 
6
6
  require 'middleman-core/util'
7
+ require 'middleman-core/logger'
7
8
  require 'middleman-core/template_renderer'
8
9
 
9
10
  # CSSPIE HTC File
@@ -73,8 +74,9 @@ module Middleman
73
74
  # message.
74
75
  #
75
76
  # @param env
77
+ # @param [Rack::Request] req
76
78
  # @param [Rack::Response] res
77
- def process_request(env, _, res)
79
+ def process_request(env, req, res)
78
80
  start_time = Time.now
79
81
 
80
82
  request_path = URI.decode(env['PATH_INFO'].dup)
@@ -100,7 +102,7 @@ module Middleman
100
102
 
101
103
  begin
102
104
  # Write out the contents of the page
103
- res.write resource.render
105
+ res.write resource.render({}, { rack: { request: req } })
104
106
 
105
107
  # Valid content is a 200 status
106
108
  res.status = 200
@@ -124,7 +126,7 @@ module Middleman
124
126
  # Immediately send static file
125
127
  def send_file(resource, env)
126
128
  file = ::Rack::File.new nil
127
- file.path = resource.source_file[:full_path]
129
+ file.path = resource.file_descriptor[:full_path]
128
130
  response = file.serving(env)
129
131
  status = response[0]
130
132
  response[1]['Content-Encoding'] = 'gzip' if %w(.svgz .gz).include?(resource.ext)
@@ -20,29 +20,45 @@ module Middleman
20
20
  # thus making it impossible to pass our Middleman instance
21
21
  # in. So we have to resort to heavy hackery :(
22
22
  class HamlTemplate < ::Tilt::HamlTemplate
23
+ def initialize(*args, &block)
24
+ super
25
+
26
+ @context = @options[:context] if @options.key?(:context)
27
+ end
28
+
23
29
  def prepare
24
30
  end
25
31
 
26
32
  def evaluate(scope, locals, &block)
27
- ::Middleman::Renderers::Haml.last_haml_scope = scope
28
-
29
- options = @options.merge(filename: eval_file, line: line)
33
+ options = @options.merge(filename: eval_file, line: line, context: @context || scope)
30
34
  @engine = ::Haml::Engine.new(data, options)
31
35
  output = @engine.render(scope, locals, &block)
32
36
 
33
- ::Middleman::Renderers::Haml.last_haml_scope = nil
34
-
35
37
  output
36
38
  end
37
39
  end
38
40
 
39
41
  # Haml Renderer
40
42
  class Haml < ::Middleman::Extension
41
- cattr_accessor :last_haml_scope
42
43
 
43
44
  def initialize(app, options={}, &block)
44
45
  super
45
46
 
47
+ ::Haml::Options.defaults[:context] = nil
48
+ ::Haml::Options.send :attr_accessor, :context
49
+
50
+ [::Haml::Filters::Sass, ::Haml::Filters::Scss, ::Haml::Filters::Markdown].each do |f|
51
+ f.class_exec do
52
+ def self.render_with_options(text, compiler_options)
53
+ modified_options = options.dup
54
+ modified_options[:context] = compiler_options[:context]
55
+
56
+ text = template_class.new(nil, 1, modified_options) {text}.render
57
+ super(text, compiler_options)
58
+ end
59
+ end
60
+ end
61
+
46
62
  ::Tilt.prefer(::Middleman::Renderers::HamlTemplate, :haml)
47
63
 
48
64
  # Add haml helpers to context
@@ -4,10 +4,16 @@ module Middleman
4
4
  module Renderers
5
5
  # Our own Kramdown Tilt template that simply uses our custom renderer.
6
6
  class KramdownTemplate < ::Tilt::KramdownTemplate
7
- def evaluate(scope, *)
8
- @output ||= begin
9
- MiddlemanKramdownHTML.scope = (defined?(::Middleman::Renderers::Haml) && ::Middleman::Renderers::Haml.last_haml_scope) ? ::Middleman::Renderers::Haml.last_haml_scope : scope
7
+ def initialize(*args, &block)
8
+ super
9
+
10
+ @context = @options[:context] if @options.key?(:context)
11
+ end
10
12
 
13
+ def evaluate(context, *)
14
+ MiddlemanKramdownHTML.scope = @context || context
15
+
16
+ @output ||= begin
11
17
  output, warnings = MiddlemanKramdownHTML.convert(@engine.root, @engine.options)
12
18
  @engine.warnings.concat(warnings)
13
19
  output
@@ -38,6 +44,7 @@ module Middleman
38
44
 
39
45
  attr = el.attr.dup
40
46
  link = attr.delete('href')
47
+
41
48
  scope.link_to(content, link, attr)
42
49
  end
43
50
  end
@@ -23,11 +23,30 @@ module Middleman
23
23
  return resources unless app.extensions[:data]
24
24
 
25
25
  resources.each do |resource|
26
- next if resource.source_file.nil?
27
- next unless resource.source_file[:relative_path].to_s =~ %r{\.liquid$}
26
+ next if resource.file_descriptor.nil?
27
+ next unless resource.file_descriptor[:relative_path].to_s =~ %r{\.liquid$}
28
28
 
29
29
  # Convert data object into a hash for liquid
30
- resource.add_metadata locals: { data: app.extensions[:data].data_store.to_h }
30
+ resource.add_metadata locals: {
31
+ data: stringify_recursive(app.extensions[:data].data_store.to_h)
32
+ }
33
+ end
34
+ end
35
+
36
+ def stringify_recursive(hash)
37
+ {}.tap do |h|
38
+ hash.each { |key, value| h[key.to_s] = map_value(value) }
39
+ end
40
+ end
41
+
42
+ def map_value(thing)
43
+ case thing
44
+ when Hash
45
+ stringify_recursive(thing)
46
+ when Array
47
+ thing.map { |v| map_value(v) }
48
+ else
49
+ thing
31
50
  end
32
51
  end
33
52
  end
@@ -10,6 +10,12 @@ module Middleman
10
10
  escape_html: :filter_html
11
11
  }
12
12
 
13
+ def initialize(*args, &block)
14
+ super
15
+
16
+ @context = @options[:context] if @options.key?(:context)
17
+ end
18
+
13
19
  # Overwrite built-in Tilt version.
14
20
  # Don't overload :renderer option with smartypants
15
21
  # Support renderer-level options
@@ -40,11 +46,7 @@ module Middleman
40
46
 
41
47
  def evaluate(scope, _)
42
48
  @output ||= begin
43
- if defined?(::Middleman::Renderers::Haml)
44
- MiddlemanRedcarpetHTML.scope = ::Middleman::Renderers::Haml.last_haml_scope || scope
45
- else
46
- MiddlemanRedcarpetHTML.scope = scope
47
- end
49
+ MiddlemanRedcarpetHTML.scope = @context || scope
48
50
 
49
51
  @engine.render(data)
50
52
  end
@@ -94,11 +94,7 @@ module Middleman
94
94
  # Change Sass path, for url functions, to the build folder if we're building
95
95
  # @return [Hash]
96
96
  def sass_options
97
- ctx = if defined?(::Middleman::Renderers::Haml)
98
- ::Middleman::Renderers::Haml.last_haml_scope || @context
99
- else
100
- @context
101
- end
97
+ ctx = @context
102
98
 
103
99
  more_opts = {
104
100
  load_paths: ctx.config[:sass_assets_paths],
@@ -7,9 +7,24 @@ module SafeTemplate
7
7
  end
8
8
  end
9
9
 
10
- class Slim::Template
10
+ class ::Slim::Template
11
11
  include SafeTemplate
12
12
 
13
+ def initialize(file, line, opts, &block)
14
+ if opts.key?(:context)
15
+ context_hack = {
16
+ context: opts[:context]
17
+ }
18
+
19
+ ::Slim::Embedded::SassEngine.disable_option_validator!
20
+ %w(sass scss markdown).each do |engine|
21
+ ::Slim::Embedded.options[engine.to_sym] = context_hack
22
+ end
23
+ end
24
+
25
+ super
26
+ end
27
+
13
28
  def precompiled_preamble(locals)
14
29
  "__in_slim_template = true\n" << super
15
30
  end
@@ -32,17 +47,6 @@ module Middleman
32
47
  disable_escape: true
33
48
  )
34
49
  end
35
-
36
- def after_configuration
37
- context_hack = {
38
- context: app.template_context_class.new(app)
39
- }
40
-
41
- ::Slim::Embedded::SassEngine.disable_option_validator!
42
- %w(sass scss markdown).each do |engine|
43
- ::Slim::Embedded.options[engine.to_sym] = context_hack
44
- end
45
- end
46
50
  end
47
51
  end
48
52
  end
@@ -3,6 +3,8 @@ module Middleman
3
3
  module Extensions
4
4
  # Class to handle managing ignores
5
5
  class Ignores < Extension
6
+ self.resource_list_manipulator_priority = 0
7
+
6
8
  # Expose `create_ignore` as `app.ignore`
7
9
  expose_to_application ignore: :create_ignore
8
10
 
@@ -5,6 +5,8 @@ module Middleman
5
5
  module Sitemap
6
6
  module Extensions
7
7
  class OnDisk < Extension
8
+ self.resource_list_manipulator_priority = 0
9
+
8
10
  def initialize(app, config={}, &block)
9
11
  super
10
12