middleman-core 4.0.0.alpha.6 → 4.0.0.beta.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 (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
@@ -0,0 +1,86 @@
1
+ # CLI Module
2
+ module Middleman::Cli
3
+ # Server thor task
4
+ class Server < Thor
5
+ check_unknown_options!
6
+
7
+ namespace :server
8
+
9
+ desc 'server [options]', 'Start the preview server'
10
+ method_option :environment,
11
+ aliases: '-e',
12
+ default: ENV['MM_ENV'] || ENV['RACK_ENV'] || 'development',
13
+ desc: 'The environment Middleman will run under'
14
+ method_option :host,
15
+ type: :string,
16
+ aliases: '-h',
17
+ desc: 'Bind to HOST address'
18
+ method_option :port,
19
+ aliases: '-p',
20
+ desc: 'The port Middleman will listen on'
21
+ method_option :verbose,
22
+ type: :boolean,
23
+ default: false,
24
+ desc: 'Print debug messages'
25
+ method_option :instrument,
26
+ type: :string,
27
+ default: false,
28
+ desc: 'Print instrument messages'
29
+ method_option :disable_watcher,
30
+ type: :boolean,
31
+ default: false,
32
+ desc: 'Disable the file change and delete watcher process'
33
+ method_option :profile,
34
+ type: :boolean,
35
+ default: false,
36
+ desc: 'Generate profiling report for server startup'
37
+ method_option :reload_paths,
38
+ type: :string,
39
+ default: false,
40
+ desc: 'Additional paths to auto-reload when files change'
41
+ method_option :force_polling,
42
+ type: :boolean,
43
+ default: false,
44
+ desc: 'Force file watcher into polling mode'
45
+ method_option :latency,
46
+ type: :numeric,
47
+ aliases: '-l',
48
+ default: 0.25,
49
+ desc: 'Set file watcher latency, in seconds'
50
+
51
+ # Start the server
52
+ def server
53
+ require 'middleman-core'
54
+ require 'middleman-core/preview_server'
55
+
56
+ unless ENV['MM_ROOT']
57
+ puts '== Could not find a Middleman project config.rb'
58
+ puts '== Treating directory as a static site to be served'
59
+ ENV['MM_ROOT'] = Dir.pwd
60
+ ENV['MM_SOURCE'] = ''
61
+ end
62
+
63
+ params = {
64
+ port: options['port'],
65
+ host: options['host'],
66
+ environment: options['environment'],
67
+ debug: options['verbose'],
68
+ instrumenting: options['instrument'],
69
+ disable_watcher: options['disable_watcher'],
70
+ reload_paths: options['reload_paths'],
71
+ force_polling: options['force_polling'],
72
+ latency: options['latency']
73
+ }
74
+
75
+ puts '== The Middleman is loading'
76
+ ::Middleman::PreviewServer.start(params)
77
+ end
78
+ end
79
+
80
+ def self.exit_on_failure?
81
+ true
82
+ end
83
+
84
+ # Map "s" to "server"
85
+ Base.map('s' => 'server')
86
+ end
@@ -7,7 +7,7 @@ module Middleman
7
7
  attr_reader :app
8
8
 
9
9
  # Whitelist methods that can reach out.
10
- def_delegators :@app, :config, :logger, :use, :map, :mime_type, :data, :files, :root, :build?, :server?, :environment?
10
+ def_delegators :@app, :config, :logger, :use, :map, :mime_type, :files, :root, :build?, :server?, :environment?
11
11
  def_delegator :"@app.extensions", :activate
12
12
 
13
13
  def initialize(app, template_context_class)
@@ -27,38 +27,6 @@ if ENV['TEST'] || ENV['CONTRACTS'] == 'true'
27
27
  end
28
28
  end
29
29
 
30
- class ArrayOf
31
- def initialize(contract)
32
- @contract = contract.is_a?(String) ? IsA[contract] : contract
33
- end
34
- end
35
-
36
- class SetOf < CallableClass
37
- def initialize(contract)
38
- @contract = contract.is_a?(String) ? IsA[contract] : contract
39
- end
40
-
41
- def valid?(vals)
42
- return false unless vals.is_a?(Set)
43
- vals.all? do |val|
44
- res, _ = Contract.valid?(val, @contract)
45
- res
46
- end
47
- end
48
-
49
- def to_s
50
- "a set of #{@contract}"
51
- end
52
-
53
- def testable?
54
- Testable.testable? @contract
55
- end
56
-
57
- def test_data
58
- Set.new([], [Testable.test_data(@contract)], [Testable.test_data(@contract), Testable.test_data(@contract)])
59
- end
60
- end
61
-
62
30
  # class MethodDefined
63
31
  # def self.[](val)
64
32
  # @lookup ||= {}
@@ -16,7 +16,21 @@ module Middleman
16
16
  # gets a chance to modify any new resources that get added.
17
17
  self.resource_list_manipulator_priority = 110
18
18
 
19
- attr_accessor :root_collector, :leaves
19
+ attr_accessor :sitemap_collector, :data_collector, :leaves
20
+
21
+ # Expose `resources`, `data`, and `collection` to config.
22
+ expose_to_config resources: :sitemap_collector,
23
+ data: :data_collector,
24
+ collection: :register_collector
25
+
26
+ # Exposes `collection` to templates
27
+ expose_to_template collection: :collector_value
28
+
29
+ helpers do
30
+ def pagination
31
+ current_resource.data.pagination
32
+ end
33
+ end
20
34
 
21
35
  def initialize(app, options_hash={}, &block)
22
36
  super
@@ -25,15 +39,12 @@ module Middleman
25
39
  @collectors_by_name = {}
26
40
  @values_by_name = {}
27
41
 
28
- @root_collector = LazyCollectorRoot.new(self)
42
+ @sitemap_collector = LazyCollectorRoot.new(self)
43
+ @data_collector = LazyCollectorRoot.new(self)
29
44
  end
30
45
 
31
- Contract None => Any
32
46
  def before_configuration
33
47
  @leaves.clear
34
-
35
- app.add_to_config_context :resources, &method(:root_collector)
36
- app.add_to_config_context :collection, &method(:register_collector)
37
48
  end
38
49
 
39
50
  Contract Symbol, LazyCollectorStep => Any
@@ -48,7 +59,8 @@ module Middleman
48
59
 
49
60
  Contract ResourceList => ResourceList
50
61
  def manipulate_resource_list(resources)
51
- @root_collector.realize!(resources)
62
+ @sitemap_collector.realize!(resources)
63
+ @data_collector.realize!(app.data)
52
64
 
53
65
  ctx = StepContext.new
54
66
  leaves = @leaves.dup
@@ -66,16 +78,6 @@ module Middleman
66
78
  # Inject descriptors
67
79
  resources + ctx.descriptors.map { |d| d.to_resource(app) }
68
80
  end
69
-
70
- helpers do
71
- def collection(label)
72
- extensions[:collections].collector_value(label)
73
- end
74
-
75
- def pagination
76
- current_resource.data.pagination
77
- end
78
- end
79
81
  end
80
82
  end
81
83
  end
@@ -9,17 +9,21 @@ module Middleman
9
9
  class Data < Extension
10
10
  attr_reader :data_store
11
11
 
12
+ # Make the internal `data_store` method available as `app.data`
13
+ expose_to_application data: :data_store
14
+
15
+ # Exposes `data` to templates
16
+ expose_to_template data: :data_store
17
+
12
18
  # The regex which tells Middleman which files are for data
13
19
  DATA_FILE_MATCHER = /^(.*?)[\w-]+\.(yml|yaml|json)$/
14
20
 
15
21
  def initialize(app, config={}, &block)
16
22
  super
23
+
17
24
  @data_store = DataStore.new(app, DATA_FILE_MATCHER)
18
25
  app.config.define_setting :data_dir, 'data', 'The directory data files are stored in'
19
26
 
20
- app.add_to_instance(:data, &method(:data_store))
21
- app.add_to_config_context(:data, &method(:data_store))
22
-
23
27
  start_watching(app.config[:data_dir])
24
28
  end
25
29
 
@@ -29,7 +33,7 @@ module Middleman
29
33
  # Tell the file watcher to observe the :data_dir
30
34
  @watcher = app.files.watch :data,
31
35
  path: File.join(app.root, dir),
32
- ignore: proc { |f| !DATA_FILE_MATCHER.match(f[:relative_path]) }
36
+ only: DATA_FILE_MATCHER
33
37
 
34
38
  # Setup data files before anything else so they are available when
35
39
  # parsing config.rb
@@ -42,12 +46,6 @@ module Middleman
42
46
  @watcher.update_path(app.config[:data_dir])
43
47
  end
44
48
 
45
- helpers do
46
- def data
47
- extensions[:data].data_store
48
- end
49
- end
50
-
51
49
  # The core logic behind the data extension.
52
50
  class DataStore
53
51
  include Contracts
@@ -79,7 +77,7 @@ module Middleman
79
77
  # @param [Symbol] name Name of the data, used for namespacing
80
78
  # @param [Proc] proc The callback which will return data
81
79
  # @return [Hash]
82
- Contract Symbol, Proc => Hash
80
+ Contract Maybe[Symbol], Maybe[Proc] => Hash
83
81
  def callbacks(name=nil, proc=nil)
84
82
  @callback_sources[name.to_s] = proc unless name.nil? || proc.nil?
85
83
  @callback_sources
@@ -102,9 +100,9 @@ module Middleman
102
100
  basename = File.basename(data_path, extension)
103
101
 
104
102
  if %w(.yaml .yml).include?(extension)
105
- data = YAML.load_file(file[:full_path])
103
+ data = ::YAML.load_file(file[:full_path])
106
104
  elsif extension == '.json'
107
- data = ActiveSupport::JSON.decode(file[:full_path].read)
105
+ data = ::ActiveSupport::JSON.decode(file[:full_path].read)
108
106
  else
109
107
  return
110
108
  end
@@ -162,8 +160,8 @@ module Middleman
162
160
  # @return [Hash, nil]
163
161
  def method_missing(path)
164
162
  if @local_data.key?(path.to_s)
165
- @local_data[path.to_s] = ::Middleman::Util.recursively_enhance(@local_data[path.to_s])
166
- return @local_data[path.to_s]
163
+ # Any way to cache this?
164
+ return ::Middleman::Util.recursively_enhance(@local_data[path.to_s])
167
165
  else
168
166
  result = data_for_path(path)
169
167
  return result if result
@@ -187,7 +185,7 @@ module Middleman
187
185
  end
188
186
 
189
187
  def key?(key)
190
- @local_data.key?(key.to_s) || data_for_path(key)
188
+ (@local_data.keys + @local_sources.keys + @callback_sources.keys).include?(key.to_s)
191
189
  end
192
190
 
193
191
  alias_method :has_key?, :key?
@@ -195,7 +193,7 @@ module Middleman
195
193
  # Convert all the data into a static hash
196
194
  #
197
195
  # @return [Hash]
198
- Contract None => Hash
196
+ Contract Hash
199
197
  def to_h
200
198
  data = {}
201
199
 
@@ -68,7 +68,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
68
68
  block.call(*args)
69
69
  end
70
70
 
71
- ActiveSupport::SafeBuffer.new.safe_concat(result)
71
+ ::ActiveSupport::SafeBuffer.new.safe_concat(result)
72
72
  end
73
73
 
74
74
  def auto_find_proper_handler(&block)
@@ -221,5 +221,26 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
221
221
  url = url_for(url, options)
222
222
  super
223
223
  end
224
+
225
+ # Modified Padrino image_tag so that it finds the paths for srcset
226
+ # using asset_path for the images listed in the srcset param
227
+ def image_tag(path, params={})
228
+ params.symbolize_keys!
229
+
230
+ if params.key?(:srcset)
231
+ images_sources = params[:srcset].split(',').map do |src_def|
232
+ if src_def.include?('//')
233
+ src_def
234
+ else
235
+ image_def, size_def = src_def.strip.split(/\s+/)
236
+ asset_path(:images, image_def) + ' ' + size_def
237
+ end
238
+ end
239
+
240
+ params[:srcset] = images_sources.join(', ')
241
+ end
242
+
243
+ super(path, params)
244
+ end
224
245
  end
225
246
  end
@@ -6,9 +6,15 @@ module Middleman
6
6
  # API for watching file change events
7
7
  class FileWatcher < Extension
8
8
  # All defined sources.
9
- Contract None => IsA['Middleman::Sources']
9
+ Contract IsA['Middleman::Sources']
10
10
  attr_reader :sources
11
11
 
12
+ # Make the internal `sources` method available as `app.files`
13
+ expose_to_application files: :sources
14
+
15
+ # Make the internal `sources` method available in config as `files`
16
+ expose_to_config files: :sources
17
+
12
18
  # The default list of ignores.
13
19
  IGNORES = {
14
20
  emacs_files: /(^|\/)\.?#/,
@@ -34,16 +40,12 @@ module Middleman
34
40
 
35
41
  # Watch current source.
36
42
  start_watching(app.config[:source])
37
-
38
- # Expose API to app and config.
39
- app.add_to_instance(:files, &method(:sources))
40
- app.add_to_config_context(:files, &method(:sources))
41
43
  end
42
44
 
43
45
  # Before we config, find initial files.
44
46
  #
45
47
  # @return [void]
46
- Contract None => Any
48
+ Contract Any
47
49
  def before_configuration
48
50
  @sources.find_new_files!
49
51
  end
@@ -51,7 +53,7 @@ module Middleman
51
53
  # After we config, find new files since config can change paths.
52
54
  #
53
55
  # @return [void]
54
- Contract None => Any
56
+ Contract Any
55
57
  def after_configuration
56
58
  if @original_source_dir != app.config[:source]
57
59
  @watcher.update_path(app.config[:source])
@@ -83,7 +83,7 @@ module Middleman::CoreExtensions
83
83
 
84
84
  # Get the frontmatter and plain content from a file
85
85
  # @param [String] path
86
- # @return [Array<Middleman::Util::HashWithIndifferentAccess, String>]
86
+ # @return [Array<Middleman::Util::IndifferentHash, String>]
87
87
  Contract Pathname => [Hash, Maybe[String]]
88
88
  def frontmatter_and_content(full_path)
89
89
  data = {}
@@ -154,7 +154,7 @@ module Middleman::CoreExtensions
154
154
 
155
155
  begin
156
156
  json = ($1 + $2).sub(';;;', '{').sub(';;;', '}')
157
- data = ActiveSupport::JSON.decode(json).symbolize_keys
157
+ data = ::ActiveSupport::JSON.decode(json).symbolize_keys
158
158
  rescue => e
159
159
  app.logger.error "JSON Exception parsing #{full_path}: #{e.message}"
160
160
  return nil
@@ -7,6 +7,9 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
7
7
  option :mount_at_root, nil, 'Mount a specific language at the root of the site'
8
8
  option :data, 'locales', 'The directory holding your locale configurations'
9
9
 
10
+ # Exposes `langs` to templates
11
+ expose_to_template :langs
12
+
10
13
  def after_configuration
11
14
  # See https://github.com/svenfuchs/i18n/wiki/Fallbacks
12
15
  unless options[:no_fallbacks]
@@ -19,7 +22,7 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
19
22
  # Tell the file watcher to observe the :data_dir
20
23
  app.files.watch :locales,
21
24
  path: File.join(app.root, locales_file_path),
22
- ignore: proc { |f| !(/.*(rb|yml|yaml)$/.match(f[:relative_path])) }
25
+ only: /.*(rb|yml|yaml)$/
23
26
 
24
27
  # Setup data files before anything else so they are available when
25
28
  # parsing config.rb
@@ -41,7 +44,7 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
41
44
  ::I18n.t(*args)
42
45
  end
43
46
 
44
- def locate_partial(partial_name)
47
+ def locate_partial(partial_name, try_static=false)
45
48
  locals_dir = extensions[:i18n].options[:templates_dir]
46
49
 
47
50
  # Try /localizable
@@ -58,18 +61,18 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
58
61
  end
59
62
 
60
63
  if lang_suffix
61
- super(suffixed_partial_name) ||
62
- super(File.join(locals_dir, suffixed_partial_name)) ||
63
- super(partials_path) ||
64
+ super(suffixed_partial_name, maybe_static) ||
65
+ super(File.join(locals_dir, suffixed_partial_name), maybe_static) ||
66
+ super(partials_path, try_static) ||
64
67
  super
65
68
  else
66
- super(partials_path) ||
69
+ super(partials_path, try_static) ||
67
70
  super
68
71
  end
69
72
  end
70
73
  end
71
74
 
72
- Contract None => ArrayOf[Symbol]
75
+ Contract ArrayOf[Symbol]
73
76
  def langs
74
77
  @langs ||= known_languages
75
78
  end
@@ -107,7 +110,6 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
107
110
 
108
111
  private
109
112
 
110
- Contract Any, Any => Any
111
113
  def on_file_changed(_updated_files, _removed_files)
112
114
  @_langs = nil # Clear langs cache
113
115
 
@@ -125,7 +127,7 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
125
127
  ::I18n.fallbacks = ::I18n::Locale::Fallbacks.new if ::I18n.respond_to?(:fallbacks)
126
128
  end
127
129
 
128
- Contract None => ArrayOf[Symbol]
130
+ Contract ArrayOf[Symbol]
129
131
  def known_languages
130
132
  if options[:langs]
131
133
  Array(options[:langs]).map(&:to_sym)
@@ -6,16 +6,15 @@ module Middleman
6
6
  # so it can add metadata to any pages generated by other extensions
7
7
  self.resource_list_manipulator_priority = 80
8
8
 
9
+ # Expose the `page` method to config.
10
+ expose_to_config :page
11
+
9
12
  def initialize(app, options_hash={}, &block)
10
13
  super
11
14
 
12
15
  @page_configs = Set.new
13
16
  end
14
17
 
15
- def before_configuration
16
- app.add_to_config_context(:page, &method(:page))
17
- end
18
-
19
18
  # @return Array<Middleman::Sitemap::Resource>
20
19
  Contract ResourceList => ResourceList
21
20
  def manipulate_resource_list(resources)