middleman-core 4.0.0.beta.1 → 4.0.0.beta.2
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.
- checksums.yaml +4 -4
- data/lib/middleman-core/application.rb +126 -112
- data/lib/middleman-core/builder.rb +7 -17
- data/lib/middleman-core/callback_manager.rb +61 -0
- data/lib/middleman-core/cli/server.rb +10 -0
- data/lib/middleman-core/config_context.rb +6 -46
- data/lib/middleman-core/contracts.rb +4 -17
- data/lib/middleman-core/core_extensions/collections/step_context.rb +4 -2
- data/lib/middleman-core/core_extensions/i18n.rb +19 -0
- data/lib/middleman-core/core_extensions/show_exceptions.rb +1 -1
- data/lib/middleman-core/extension.rb +1 -0
- data/lib/middleman-core/extension_manager.rb +11 -0
- data/lib/middleman-core/file_renderer.rb +5 -15
- data/lib/middleman-core/preview_server.rb +31 -2
- data/lib/middleman-core/rack.rb +1 -1
- data/lib/middleman-core/renderers/coffee_script.rb +1 -3
- data/lib/middleman-core/sitemap/extensions/ignores.rb +1 -1
- data/lib/middleman-core/sitemap/extensions/on_disk.rb +9 -10
- data/lib/middleman-core/sitemap/store.rb +20 -9
- data/lib/middleman-core/sources.rb +10 -9
- data/lib/middleman-core/sources/source_watcher.rb +7 -27
- data/lib/middleman-core/version.rb +1 -1
- data/middleman-core.gemspec +0 -1
- data/spec/middleman-core/callbacks_spec.rb +132 -0
- metadata +4 -16
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'hamster'
|
2
|
+
require 'middleman-core/contracts'
|
3
|
+
|
4
|
+
# Immutable Callback Management, complete with Contracts validation.
|
5
|
+
module Middleman
|
6
|
+
class CallbackManager
|
7
|
+
include Contracts
|
8
|
+
|
9
|
+
Contract Any
|
10
|
+
def initialize
|
11
|
+
@callbacks = ::Hamster.hash
|
12
|
+
@subscribers = ::Hamster.vector
|
13
|
+
end
|
14
|
+
|
15
|
+
Contract RespondTo[:define_singleton_method], ArrayOf[Symbol] => Any
|
16
|
+
def install_methods!(install_target, names)
|
17
|
+
manager = self
|
18
|
+
|
19
|
+
names.each do |method_name|
|
20
|
+
install_target.define_singleton_method(method_name) do |*keys, &b|
|
21
|
+
key_set = keys.unshift(method_name)
|
22
|
+
manager.add(key_set.length > 1 ? key_set : key_set[0], &b)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
install_target.define_singleton_method(:execute_callbacks) do |*args|
|
27
|
+
keys = args.shift
|
28
|
+
manager.execute(keys, args[0], self)
|
29
|
+
end
|
30
|
+
|
31
|
+
install_target.define_singleton_method(:callbacks_for, &method(:callbacks_for))
|
32
|
+
install_target.define_singleton_method(:subscribe_to_callbacks, &method(:subscribe))
|
33
|
+
end
|
34
|
+
|
35
|
+
Contract Or[Symbol, ArrayOf[Symbol]], Proc => Any
|
36
|
+
def add(keys, &block)
|
37
|
+
immutable_keys = keys.is_a?(Symbol) ? keys : ::Hamster::Vector.new(keys)
|
38
|
+
|
39
|
+
@callbacks = @callbacks.put(immutable_keys) do |v|
|
40
|
+
v.nil? ? ::Hamster::Vector.new([block]) : v.push(block)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
Contract Proc => Any
|
45
|
+
def subscribe(&block)
|
46
|
+
@subscribers = @subscribers.push(block)
|
47
|
+
end
|
48
|
+
|
49
|
+
Contract Or[Symbol, ArrayOf[Symbol]], Maybe[ArrayOf[Any]], Maybe[RespondTo[:instance_exec]] => Any
|
50
|
+
def execute(keys, args=[], scope=self)
|
51
|
+
callbacks_for(keys).each { |b| scope.instance_exec(*args, &b) }
|
52
|
+
@subscribers.each { |b| scope.instance_exec(keys, args, &b) }
|
53
|
+
end
|
54
|
+
|
55
|
+
Contract Or[Symbol, ArrayOf[Symbol]] => ::Hamster::Vector
|
56
|
+
def callbacks_for(keys)
|
57
|
+
immutable_keys = keys.is_a?(Symbol) ? keys : ::Hamster::Vector.new(keys)
|
58
|
+
@callbacks.get(immutable_keys) || ::Hamster.vector
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -18,6 +18,13 @@ module Middleman::Cli
|
|
18
18
|
method_option :port,
|
19
19
|
aliases: '-p',
|
20
20
|
desc: 'The port Middleman will listen on'
|
21
|
+
method_option :https,
|
22
|
+
type: :boolean,
|
23
|
+
desc: 'Serve the preview server over SSL/TLS'
|
24
|
+
method_option :ssl_certificate,
|
25
|
+
desc: 'Path to an X.509 certificate to use for the preview server'
|
26
|
+
method_option :ssl_private_key,
|
27
|
+
desc: "Path to an RSA private key for the preview server's certificate"
|
21
28
|
method_option :verbose,
|
22
29
|
type: :boolean,
|
23
30
|
default: false,
|
@@ -63,6 +70,9 @@ module Middleman::Cli
|
|
63
70
|
params = {
|
64
71
|
port: options['port'],
|
65
72
|
host: options['host'],
|
73
|
+
https: options['https'],
|
74
|
+
ssl_certificate: options['ssl_certificate'],
|
75
|
+
ssl_private_key: options['ssl_private_key'],
|
66
76
|
environment: options['environment'],
|
67
77
|
debug: options['verbose'],
|
68
78
|
instrumenting: options['instrument'],
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rack/mime'
|
2
|
+
require 'middleman-core/callback_manager'
|
2
3
|
|
3
4
|
module Middleman
|
4
5
|
class ConfigContext
|
@@ -14,10 +15,11 @@ module Middleman
|
|
14
15
|
@app = app
|
15
16
|
@template_context_class = template_context_class
|
16
17
|
|
17
|
-
@
|
18
|
-
@
|
19
|
-
|
20
|
-
|
18
|
+
@callbacks = ::Middleman::CallbackManager.new
|
19
|
+
@callbacks.install_methods!(self, [:before_build, :after_build, :configure, :after_configuration, :ready])
|
20
|
+
|
21
|
+
# Trigger internal callbacks when app level are executed.
|
22
|
+
app.subscribe_to_callbacks(&method(:execute_callbacks))
|
21
23
|
end
|
22
24
|
|
23
25
|
def helpers(*helper_modules, &block)
|
@@ -43,48 +45,6 @@ module Middleman
|
|
43
45
|
instance_eval File.read(other_config), other_config, 1
|
44
46
|
end
|
45
47
|
|
46
|
-
def ready(&block)
|
47
|
-
@ready_callbacks << block
|
48
|
-
end
|
49
|
-
|
50
|
-
def execute_ready_callbacks
|
51
|
-
@ready_callbacks.each do |b|
|
52
|
-
instance_exec(&b)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def after_build(&block)
|
57
|
-
@after_build_callbacks << block
|
58
|
-
end
|
59
|
-
|
60
|
-
def execute_after_build_callbacks(*args)
|
61
|
-
@after_build_callbacks.each do |b|
|
62
|
-
instance_exec(*args, &b)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def after_configuration(&block)
|
67
|
-
@after_configuration_callbacks << block
|
68
|
-
end
|
69
|
-
|
70
|
-
def execute_after_configuration_callbacks
|
71
|
-
@after_configuration_callbacks.each do |b|
|
72
|
-
instance_exec(&b)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def configure(key, &block)
|
77
|
-
@configure_callbacks[key] ||= []
|
78
|
-
@configure_callbacks[key] << block
|
79
|
-
end
|
80
|
-
|
81
|
-
def execute_configure_callbacks(key)
|
82
|
-
@configure_callbacks[key] ||= []
|
83
|
-
@configure_callbacks[key].each do |b|
|
84
|
-
instance_exec(&b)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
48
|
def set(key, default=nil, &block)
|
89
49
|
config.define_setting(key, default) unless config.defines_setting?(key)
|
90
50
|
@app.config[key] = block_given? ? block : default
|
@@ -1,5 +1,6 @@
|
|
1
1
|
if ENV['TEST'] || ENV['CONTRACTS'] == 'true'
|
2
2
|
require 'contracts'
|
3
|
+
require 'hamster'
|
3
4
|
|
4
5
|
module Contracts
|
5
6
|
class IsA
|
@@ -27,21 +28,7 @@ if ENV['TEST'] || ENV['CONTRACTS'] == 'true'
|
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
30
|
-
|
31
|
-
# def self.[](val)
|
32
|
-
# @lookup ||= {}
|
33
|
-
# @lookup[val] ||= new(val)
|
34
|
-
# end
|
35
|
-
|
36
|
-
# def initialize(val)
|
37
|
-
# @val = val
|
38
|
-
# end
|
39
|
-
|
40
|
-
# def valid?(val)
|
41
|
-
# val.method_defined? @val
|
42
|
-
# end
|
43
|
-
# end
|
44
|
-
|
31
|
+
VectorOf = Contracts::CollectionOf::Factory.new(::Hamster::Vector)
|
45
32
|
ResourceList = Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']]
|
46
33
|
end
|
47
34
|
else
|
@@ -125,8 +112,8 @@ else
|
|
125
112
|
class Frozen < Callable
|
126
113
|
end
|
127
114
|
|
128
|
-
|
129
|
-
|
115
|
+
class VectorOf < Callable
|
116
|
+
end
|
130
117
|
end
|
131
118
|
end
|
132
119
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'hamster'
|
2
|
+
|
1
3
|
module Middleman
|
2
4
|
module CoreExtensions
|
3
5
|
module Collections
|
@@ -9,13 +11,13 @@ module Middleman
|
|
9
11
|
attr_reader :descriptors
|
10
12
|
|
11
13
|
def initialize
|
12
|
-
@descriptors =
|
14
|
+
@descriptors = ::Hamster.set
|
13
15
|
end
|
14
16
|
|
15
17
|
def method_missing(name, *args, &block)
|
16
18
|
internal = :"_internal_#{name}"
|
17
19
|
if respond_to?(internal)
|
18
|
-
@descriptors
|
20
|
+
@descriptors = @descriptors.add(send(internal, *args, &block))
|
19
21
|
else
|
20
22
|
super
|
21
23
|
end
|
@@ -10,6 +10,25 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
|
|
10
10
|
# Exposes `langs` to templates
|
11
11
|
expose_to_template :langs
|
12
12
|
|
13
|
+
def initialize(*)
|
14
|
+
super
|
15
|
+
|
16
|
+
require 'i18n'
|
17
|
+
|
18
|
+
# Don't fail on invalid locale, that's not what our current
|
19
|
+
# users expect.
|
20
|
+
::I18n.enforce_available_locales = false
|
21
|
+
|
22
|
+
# This is for making the tests work - since the tests
|
23
|
+
# don't completely reload middleman, I18n.load_path can get
|
24
|
+
# polluted with paths from other test app directories that don't
|
25
|
+
# exist anymore.
|
26
|
+
app.after_configuration_eval do
|
27
|
+
::I18n.load_path.delete_if { |path| path =~ %r{tmp/aruba} }
|
28
|
+
::I18n.reload!
|
29
|
+
end if ENV['TEST']
|
30
|
+
end
|
31
|
+
|
13
32
|
def after_configuration
|
14
33
|
# See https://github.com/svenfuchs/i18n/wiki/Fallbacks
|
15
34
|
unless options[:no_fallbacks]
|
@@ -8,7 +8,7 @@ module Middleman::CoreExtensions
|
|
8
8
|
|
9
9
|
return if app.config.defines_setting? :show_exceptions
|
10
10
|
|
11
|
-
app.config.define_setting :show_exceptions,
|
11
|
+
app.config.define_setting :show_exceptions, ENV['TEST'] ? false : true, 'Whether to catch and display exceptions'
|
12
12
|
end
|
13
13
|
|
14
14
|
def after_configuration
|
@@ -8,6 +8,17 @@ module Middleman
|
|
8
8
|
def initialize(app)
|
9
9
|
@app = app
|
10
10
|
@activated = {}
|
11
|
+
|
12
|
+
manager = self
|
13
|
+
{
|
14
|
+
before_sitemap: :before_sitemap,
|
15
|
+
initialized: :before_configuration
|
16
|
+
}.each do |key, value|
|
17
|
+
cb = proc { manager.auto_activate(value) }
|
18
|
+
@app.send(key, &cb)
|
19
|
+
end
|
20
|
+
|
21
|
+
@app.after_configuration_eval(&method(:activate_all))
|
11
22
|
end
|
12
23
|
|
13
24
|
def auto_activate(key)
|
@@ -59,14 +59,10 @@ module Middleman
|
|
59
59
|
options = options.deep_merge(options[:renderer_options]) if options[:renderer_options]
|
60
60
|
|
61
61
|
template_class = ::Tilt[path]
|
62
|
+
|
62
63
|
# Allow hooks to manipulate the template before render
|
63
|
-
@app.
|
64
|
-
|
65
|
-
callback.call(body, path, locs, template_class)
|
66
|
-
elsif callback.respond_to?(:evaluate)
|
67
|
-
callback.evaluate(self, body, path, locs, template_class)
|
68
|
-
end
|
69
|
-
body = newbody if newbody # Allow the callback to return nil to skip it
|
64
|
+
body = @app.callbacks_for(:before_render).reduce(body) do |sum, callback|
|
65
|
+
callback.call(sum, path, locs, template_class) || sum
|
70
66
|
end
|
71
67
|
|
72
68
|
# Read compiled template from disk or cache
|
@@ -80,14 +76,8 @@ module Middleman
|
|
80
76
|
end
|
81
77
|
|
82
78
|
# Allow hooks to manipulate the result after render
|
83
|
-
@app.
|
84
|
-
|
85
|
-
newcontent = if callback.respond_to?(:call)
|
86
|
-
callback.call(content, path, locs, template_class)
|
87
|
-
elsif callback.respond_to?(:evaluate)
|
88
|
-
callback.evaluate(self, content, path, locs, template_class)
|
89
|
-
end
|
90
|
-
content = newcontent if newcontent # Allow the callback to return nil to skip it
|
79
|
+
content = @app.callbacks_for(:before_render).reduce(content) do |sum, callback|
|
80
|
+
callback.call(sum, path, locs, template_class) || sum
|
91
81
|
end
|
92
82
|
|
93
83
|
output = ::ActiveSupport::SafeBuffer.new ''
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'webrick'
|
2
|
+
require 'webrick/https'
|
3
|
+
require 'openssl'
|
2
4
|
require 'middleman-core/meta_pages'
|
3
5
|
require 'middleman-core/logger'
|
4
6
|
require 'middleman-core/rack'
|
@@ -9,9 +11,13 @@ module Middleman
|
|
9
11
|
class << self
|
10
12
|
extend Forwardable
|
11
13
|
|
12
|
-
attr_reader :app, :host, :port
|
14
|
+
attr_reader :app, :host, :port, :ssl_certificate, :ssl_private_key
|
13
15
|
def_delegator :app, :logger
|
14
16
|
|
17
|
+
def https?
|
18
|
+
@https
|
19
|
+
end
|
20
|
+
|
15
21
|
# Start an instance of Middleman::Application
|
16
22
|
# @return [void]
|
17
23
|
def start(opts={})
|
@@ -105,6 +111,8 @@ module Middleman
|
|
105
111
|
|
106
112
|
config[:host] = opts[:host] if opts[:host]
|
107
113
|
config[:port] = opts[:port] if opts[:port]
|
114
|
+
config[:ssl_certificate] = opts[:ssl_certificate] if opts[:ssl_certificate]
|
115
|
+
config[:ssl_private_key] = opts[:ssl_private_key] if opts[:ssl_private_key]
|
108
116
|
|
109
117
|
ready do
|
110
118
|
match_against = [
|
@@ -123,6 +131,10 @@ module Middleman
|
|
123
131
|
|
124
132
|
@host = app.config[:host]
|
125
133
|
@port = app.config[:port]
|
134
|
+
@https = app.config[:https]
|
135
|
+
|
136
|
+
@ssl_certificate = app.config[:ssl_certificate]
|
137
|
+
@ssl_private_key = app.config[:ssl_private_key]
|
126
138
|
|
127
139
|
app.files.on_change :reload do
|
128
140
|
$mm_reload = true
|
@@ -163,6 +175,22 @@ module Middleman
|
|
163
175
|
DoNotReverseLookup: true
|
164
176
|
}
|
165
177
|
|
178
|
+
if https?
|
179
|
+
http_opts[:SSLEnable] = true
|
180
|
+
|
181
|
+
if ssl_certificate || ssl_private_key
|
182
|
+
raise 'You must provide both :ssl_certificate and :ssl_private_key' unless ssl_private_key && ssl_certificate
|
183
|
+
http_opts[:SSLCertificate] = OpenSSL::X509::Certificate.new File.read ssl_certificate
|
184
|
+
http_opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new File.read ssl_private_key
|
185
|
+
else
|
186
|
+
# use a generated self-signed cert
|
187
|
+
http_opts[:SSLCertName] = [
|
188
|
+
%w(CN localhost),
|
189
|
+
%w(CN #{host})
|
190
|
+
].uniq
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
166
194
|
if is_logging
|
167
195
|
http_opts[:Logger] = FilteredWebrickLog.new
|
168
196
|
else
|
@@ -203,7 +231,8 @@ module Middleman
|
|
203
231
|
# @return [URI]
|
204
232
|
def uri
|
205
233
|
host = (@host == '0.0.0.0') ? 'localhost' : @host
|
206
|
-
|
234
|
+
scheme = https? ? 'https' : 'http'
|
235
|
+
URI("#{scheme}://#{host}:#{@port}")
|
207
236
|
end
|
208
237
|
end
|
209
238
|
|
data/lib/middleman-core/rack.rb
CHANGED
@@ -85,7 +85,7 @@ module Middleman
|
|
85
85
|
full_request_path = File.join(env['SCRIPT_NAME'], request_path) # Path including rack mount
|
86
86
|
|
87
87
|
# Run before callbacks
|
88
|
-
@middleman.
|
88
|
+
@middleman.execute_callbacks(:before)
|
89
89
|
|
90
90
|
# Get the resource object for this path
|
91
91
|
resource = @middleman.sitemap.find_resource_by_destination_path(request_path.gsub(' ', '%20'))
|
@@ -13,9 +13,7 @@ module Middleman
|
|
13
13
|
::Tilt.register 'coffee', DebuggingCoffeeScriptTemplate
|
14
14
|
::Tilt.prefer(DebuggingCoffeeScriptTemplate)
|
15
15
|
|
16
|
-
|
17
|
-
DebuggingCoffeeScriptTemplate.middleman_app = self
|
18
|
-
end
|
16
|
+
DebuggingCoffeeScriptTemplate.middleman_app = app
|
19
17
|
end
|
20
18
|
|
21
19
|
# A Template for Tilt which outputs debug messages
|
@@ -12,7 +12,7 @@ module Middleman
|
|
12
12
|
def initialize(app, config={}, &block)
|
13
13
|
super
|
14
14
|
|
15
|
-
# Array of callbacks which can
|
15
|
+
# Array of callbacks which can assign ignored
|
16
16
|
@ignored_callbacks = Set.new
|
17
17
|
|
18
18
|
@app.sitemap.define_singleton_method(:ignored?, &method(:ignored?))
|
@@ -5,21 +5,18 @@ module Middleman
|
|
5
5
|
module Sitemap
|
6
6
|
module Extensions
|
7
7
|
class OnDisk < Extension
|
8
|
-
attr_accessor :waiting_for_ready
|
9
|
-
|
10
8
|
def initialize(app, config={}, &block)
|
11
9
|
super
|
12
10
|
|
13
11
|
@file_paths_on_disk = Set.new
|
14
|
-
|
15
|
-
scoped_self = self
|
16
12
|
@waiting_for_ready = true
|
13
|
+
end
|
17
14
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
15
|
+
def ready
|
16
|
+
@waiting_for_ready = false
|
17
|
+
|
18
|
+
# Make sure the sitemap is ready for the first request
|
19
|
+
app.sitemap.ensure_resource_list_updated!
|
23
20
|
end
|
24
21
|
|
25
22
|
Contract Any
|
@@ -27,6 +24,7 @@ module Middleman
|
|
27
24
|
app.files.on_change(:source, &method(:update_files))
|
28
25
|
end
|
29
26
|
|
27
|
+
Contract IsA['Middleman::SourceFile'] => Bool
|
30
28
|
def ignored?(file)
|
31
29
|
@app.config[:ignored_sitemap_matchers].any? do |_, callback|
|
32
30
|
callback.call(file, @app)
|
@@ -48,9 +46,10 @@ module Middleman
|
|
48
46
|
|
49
47
|
# Force sitemap rebuild so the next request is ready to go.
|
50
48
|
# Skip this during build because the builder will control sitemap refresh.
|
51
|
-
@app.sitemap.ensure_resource_list_updated! unless waiting_for_ready || @app.build?
|
49
|
+
@app.sitemap.ensure_resource_list_updated! unless @waiting_for_ready || @app.build?
|
52
50
|
end
|
53
51
|
|
52
|
+
Contract ArrayOf[IsA['Middleman::SourceFile']]
|
54
53
|
def files_for_sitemap
|
55
54
|
@app.files.by_type(:source).files.reject(&method(:ignored?))
|
56
55
|
end
|