merge in fixes from stable
This commit is contained in:
commit
fdabd87957
|
@ -29,8 +29,6 @@ HashSyntax:
|
|||
EnforcedStyle: ruby19
|
||||
SpaceAroundEqualsInParameterDefault:
|
||||
EnforcedStyle: no_space
|
||||
BlockDelimiters:
|
||||
Enabled: false
|
||||
PerlBackrefs:
|
||||
Enabled: false
|
||||
ClassAndModuleChildren:
|
||||
|
|
|
@ -5,8 +5,6 @@ before_script:
|
|||
- bundle update
|
||||
rvm:
|
||||
- ruby-head
|
||||
- jruby-head
|
||||
- jruby-19mode
|
||||
- 2.2.2
|
||||
- 2.1
|
||||
- 2.0
|
||||
|
@ -17,13 +15,10 @@ matrix:
|
|||
fast_finish: true
|
||||
allow_failures:
|
||||
- rvm: ruby-head
|
||||
- rvm: jruby-19mode
|
||||
- rvm: jruby-head
|
||||
env:
|
||||
global:
|
||||
- JRUBY_OPTS='-J-Xmx1024M'
|
||||
- TEST=true
|
||||
- CODECLIMATE_REPO_TOKEN=81787f7b1c3bfa937edadcafbc94f807bf5af5c1142c7b793f2d9969a271de1f
|
||||
- CODECLIMATE_REPO_TOKEN=81787f7b1c3bfa937edadcafbc94f807bf5af5c1142c7b793f2d9969a271de1f
|
||||
script: bundle exec rake test
|
||||
notifications:
|
||||
slack: middleman:JW9OvXmn1m3XrSERe8866nBR
|
||||
|
|
10
Gemfile
10
Gemfile
|
@ -6,6 +6,7 @@ gem 'yard', '~> 0.8', require: false
|
|||
|
||||
# Test tools
|
||||
gem 'pry', '~> 0.10', group: :development, require: false
|
||||
gem 'pry-byebug'
|
||||
gem 'aruba', '~> 0.7.4', require: false
|
||||
gem 'rspec', '~> 3.0', require: false
|
||||
gem 'cucumber', '~> 2.0', require: false
|
||||
|
@ -22,18 +23,25 @@ gem 'sinatra', '>= 1.4', require: false
|
|||
gem 'redcarpet', '>= 3.1', require: false unless RUBY_ENGINE == 'jruby'
|
||||
gem 'asciidoctor', '~> 0.1', require: false
|
||||
|
||||
# Dns server to test preview server
|
||||
gem 'rubydns', '~> 1.0.1', require: false
|
||||
|
||||
# To test javascript
|
||||
gem 'poltergeist', '~> 1.6.0', require: false
|
||||
|
||||
# For less, note there is no compatible JS runtime for windows
|
||||
gem 'therubyrhino', '>= 2.0', platforms: :jruby
|
||||
gem 'therubyracer', '>= 0.12', platforms: :ruby
|
||||
|
||||
# Code Quality
|
||||
gem 'rubocop', '~> 0.24', require: false
|
||||
gem 'simplecov', '~> 0.9', require: false
|
||||
gem 'simplecov', '~> 0.10', require: false
|
||||
gem 'coveralls', '~> 0.8', require: false
|
||||
gem 'codeclimate-test-reporter', '~> 0.3', require: false, group: :test
|
||||
|
||||
# Middleman itself
|
||||
gem 'middleman-cli', path: 'middleman-cli'
|
||||
gem 'middleman-core', path: 'middleman-core'
|
||||
|
||||
# gem 'middleman-compass', github: 'middleman/middleman-compass', require: false
|
||||
# gem 'middleman-sprockets', github: 'middleman/middleman-sprockets', require: false
|
||||
|
|
|
@ -21,16 +21,14 @@ Cucumber::Rake::Task.new do |t|
|
|||
exempt_tags << '--tags ~@encoding' unless Object.const_defined?(:Encoding)
|
||||
exempt_tags << '--tags ~@nowindows' if Gem.win_platform?
|
||||
exempt_tags << '--tags ~@travishatesme' if ENV['TRAVIS'] == 'true'
|
||||
t.cucumber_opts = "--require features --color #{exempt_tags.join(' ')} --strict"
|
||||
t.cucumber_opts = "--require features --color #{exempt_tags.join(' ')} --strict" # --format #{ENV['CUCUMBER_FORMAT'] || 'Fivemat'}"
|
||||
end
|
||||
|
||||
Cucumber::Rake::Task.new(:cucumber_wip) do |t|
|
||||
exempt_tags = ['--tags @wip']
|
||||
exempt_tags << '--tags ~@nojava' if RUBY_PLATFORM == 'java'
|
||||
exempt_tags << '--tags ~@encoding' unless Object.const_defined?(:Encoding)
|
||||
exempt_tags << '--tags ~@nowindows' if Gem.win_platform?
|
||||
exempt_tags << '--tags ~@travishatesme' if ENV['TRAVIS'] == 'true'
|
||||
t.cucumber_opts = "--require features --color #{exempt_tags.join(' ')} --strict"
|
||||
t.cucumber_opts = "--require features --color #{exempt_tags.join(' ')} --strict" # --format #{ENV['CUCUMBER_FORMAT'] || 'Fivemat'}"
|
||||
end
|
||||
|
||||
require 'rspec/core/rake_task'
|
||||
|
|
|
@ -8,13 +8,22 @@ module Middleman::Cli
|
|||
aliases: '-e',
|
||||
default: ENV['MM_ENV'] || ENV['RACK_ENV'] || 'development',
|
||||
desc: 'The environment Middleman will run under'
|
||||
class_option :host,
|
||||
type: :string,
|
||||
aliases: '-h',
|
||||
desc: 'Bind to HOST address'
|
||||
class_option :port,
|
||||
aliases: '-p',
|
||||
desc: 'The port Middleman will listen on'
|
||||
class_option :server_name,
|
||||
aliases: '-s',
|
||||
desc: 'The server name Middleman will use'
|
||||
class_option :bind_address,
|
||||
aliases: '-b',
|
||||
desc: 'The bind address Middleman will listen on'
|
||||
class_option :https,
|
||||
type: :boolean,
|
||||
desc: 'Serve the preview server over SSL/TLS'
|
||||
class_option :ssl_certificate,
|
||||
desc: 'Path to an X.509 certificate to use for the preview server'
|
||||
class_option :ssl_private_key,
|
||||
desc: "Path to an RSA private key for the preview server's certificate"
|
||||
class_option :verbose,
|
||||
type: :boolean,
|
||||
default: false,
|
||||
|
@ -53,7 +62,11 @@ module Middleman::Cli
|
|||
|
||||
params = {
|
||||
port: options['port'],
|
||||
host: options['host'],
|
||||
bind_address: options['bind_address'],
|
||||
https: options['https'],
|
||||
server_name: options['server_name'],
|
||||
ssl_certificate: options['ssl_certificate'],
|
||||
ssl_private_key: options['ssl_private_key'],
|
||||
environment: options['environment'],
|
||||
debug: options['verbose'],
|
||||
instrumenting: options['instrument'],
|
||||
|
|
|
@ -1 +1 @@
|
|||
--color
|
||||
--color
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
default: --require features --tags ~@wip
|
||||
wip: --require features --tags @wip
|
||||
wip: --require features --tags @wip
|
||||
|
|
|
@ -12,7 +12,7 @@ Feature: Assets get file hashes appended to them and references to them are upda
|
|||
| images/300px-59adce76.jpg |
|
||||
| images/100px-5fd6fb90.gif |
|
||||
| javascripts/application-1d8d5276.js |
|
||||
| stylesheets/site-7474cadd.css |
|
||||
| stylesheets/site-8bc55985.css |
|
||||
| index.html |
|
||||
| subdir/index.html |
|
||||
| other/index.html |
|
||||
|
@ -26,21 +26,21 @@ Feature: Assets get file hashes appended to them and references to them are upda
|
|||
| stylesheets/site.css |
|
||||
|
||||
And the file "javascripts/application-1d8d5276.js" should contain "img.src = '/images/100px-5fd6fb90.jpg'"
|
||||
And the file "stylesheets/site-7474cadd.css" should contain:
|
||||
And the file "stylesheets/site-8bc55985.css" should contain:
|
||||
"""
|
||||
background-image: url("../images/100px-5fd6fb90.jpg")
|
||||
"""
|
||||
And the file "index.html" should contain 'href="apple-touch-icon.png"'
|
||||
And the file "index.html" should contain 'href="stylesheets/site-7474cadd.css"'
|
||||
And the file "index.html" should contain 'href="stylesheets/site-8bc55985.css"'
|
||||
And the file "index.html" should contain 'src="javascripts/application-1d8d5276.js"'
|
||||
And the file "index.html" should contain 'src="images/100px-5fd6fb90.jpg"'
|
||||
And the file "subdir/index.html" should contain 'href="../stylesheets/site-7474cadd.css"'
|
||||
And the file "subdir/index.html" should contain 'href="../stylesheets/site-8bc55985.css"'
|
||||
And the file "index.html" should contain 'srcset="images/100px-5fd6fb90.jpg 1x, images/200px-c11eb203.jpg 2x, images/300px-59adce76.jpg 3x"'
|
||||
And the file "index.html" should contain 'src="images/100px-5fd6fb90.gif"'
|
||||
And the file "index.html" should contain 'src="images/100px-1242c368.png"'
|
||||
And the file "subdir/index.html" should contain 'src="../javascripts/application-1d8d5276.js"'
|
||||
And the file "subdir/index.html" should contain 'src="../images/100px-5fd6fb90.jpg"'
|
||||
And the file "other/index.html" should contain 'href="../stylesheets/site-7474cadd.css"'
|
||||
And the file "other/index.html" should contain 'href="../stylesheets/site-8bc55985.css"'
|
||||
And the file "other/index.html" should contain 'src="../javascripts/application-1d8d5276.js"'
|
||||
And the file "other/index.html" should contain 'src="../images/100px-5fd6fb90.jpg"'
|
||||
And the file "api.json" should contain 'images/100px-5fd6fb90.gif'
|
||||
|
@ -50,6 +50,15 @@ Feature: Assets get file hashes appended to them and references to them are upda
|
|||
And the file "subdir/api.json" should contain 'images/100px-5fd6fb90.jpg'
|
||||
And the file "subdir/api.json" should contain 'images/100px-1242c368.png'
|
||||
|
||||
Scenario: Hashed fonts assets work with woff and woff2 extension
|
||||
Given a successfully built app at "asset-hash-app"
|
||||
When I cd to "build"
|
||||
Then the following files should exist:
|
||||
| fonts/fontawesome-webfont-56ce13e7.woff |
|
||||
| fonts/fontawesome-webfont-10752316.woff2 |
|
||||
And the file "stylesheets/uses_fonts-88aa3e2b.css" should contain "src: url('../fonts/fontawesome-webfont-10752316.woff2')"
|
||||
And the file "stylesheets/uses_fonts-88aa3e2b.css" should contain "url('../fonts/fontawesome-webfont-56ce13e7.woff')"
|
||||
|
||||
Scenario: Hashed assets work in preview server
|
||||
Given the Server is running at "asset-hash-app"
|
||||
When I go to "/"
|
||||
|
|
532
middleman-core/features/cli/preview_server.feature
Normal file
532
middleman-core/features/cli/preview_server.feature
Normal file
|
@ -0,0 +1,532 @@
|
|||
Feature: Run the preview server
|
||||
|
||||
As a software developer
|
||||
I want to start the preview server
|
||||
In order to view my changes immediately in the browser
|
||||
|
||||
Background:
|
||||
Given a fixture app "preview-server-app"
|
||||
And the default aruba timeout is 30 seconds
|
||||
|
||||
Scenario: Start the server with defaults
|
||||
When I run `middleman server` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://
|
||||
"""
|
||||
|
||||
Scenario: Start the server with defaults in verbose mode
|
||||
When I run `middleman server --verbose` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to ":::4567", "0.0.0.0:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://
|
||||
"""
|
||||
|
||||
@ruby-2.1
|
||||
Scenario: Start the server with defaults in verbose mode, when a local mdns server resolves the local hostname
|
||||
Given I start a mdns server for the local hostname
|
||||
When I run `middleman server --verbose` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to ":::4567", "0.0.0.0:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://
|
||||
"""
|
||||
|
||||
Scenario: Start the server with bind address 127.0.0.1
|
||||
Given I have a local hosts file with:
|
||||
"""
|
||||
# <ip-address> <hostname.domain.org> <hostname>
|
||||
127.0.0.1 localhost.localdomain localhost
|
||||
"""
|
||||
When I run `middleman server --verbose --bind-address 127.0.0.1` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to "127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://127.0.0.1:4567/__middleman"
|
||||
"""
|
||||
|
||||
Scenario: Start the server with bind address 127.0.0.1 configured via config.rb
|
||||
Given I have a local hosts file with:
|
||||
"""
|
||||
# <ip-address> <hostname.domain.org> <hostname>
|
||||
127.0.0.1 localhost.localdomain localhost
|
||||
"""
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
set :bind_address, '127.0.0.1'
|
||||
"""
|
||||
When I run `middleman server --verbose` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to "127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://127.0.0.1:4567/__middleman"
|
||||
"""
|
||||
|
||||
Scenario: Start the server with bind address 127.0.0.5
|
||||
|
||||
This will have no hostname attached because the hosts file, the DNS server
|
||||
and the MDNS-server do not know anything about 127.0.0.5
|
||||
|
||||
When I run `middleman server --verbose --bind-address 127.0.0.5` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to "127.0.0.5:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://127.0.0.5:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://127.0.0.5:4567/__middleman"
|
||||
"""
|
||||
|
||||
Scenario: Start the server with bind address ::1
|
||||
Given a file named ".hosts" with:
|
||||
"""
|
||||
# <ip-address> <hostname.domain.org> <hostname>
|
||||
::1 localhost.localdomain localhost
|
||||
"""
|
||||
When I run `middleman server --verbose --bind-address ::1` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to "::1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://[::1]:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://[::1]:4567/__middleman"
|
||||
"""
|
||||
|
||||
Scenario: Start the server with bind address 0.0.0.0
|
||||
When I run `middleman server --verbose --bind-address 0.0.0.0` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to "0.0.0.0:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://
|
||||
"""
|
||||
|
||||
Scenario: Start the server with bind address ::
|
||||
When I run `middleman server --verbose --bind-address ::` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to ":::4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://
|
||||
"""
|
||||
|
||||
Scenario: Start the server with server name "localhost"
|
||||
Given I have a local hosts file with:
|
||||
"""
|
||||
# <ip-address> <hostname.domain.org> <hostname>
|
||||
127.0.0.1 localhost.localdomain localhost
|
||||
"""
|
||||
When I run `middleman server --verbose --server-name localhost` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to "127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://localhost:4567", "http://127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://localhost:4567/__middleman", "http://127.0.0.1:4567/__middleman"
|
||||
"""
|
||||
|
||||
Scenario: Start the server with server name "localhost" configured via config.rb
|
||||
Given I have a local hosts file with:
|
||||
"""
|
||||
# <ip-address> <hostname.domain.org> <hostname>
|
||||
127.0.0.1 localhost.localdomain localhost
|
||||
"""
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
set :server_name, 'localhost'
|
||||
"""
|
||||
When I run `middleman server --verbose` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to "127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://localhost:4567", "http://127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://localhost:4567/__middleman", "http://127.0.0.1:4567/__middleman"
|
||||
"""
|
||||
|
||||
Scenario: Start the server with server name "localhost" and bind address "127.0.0.1"
|
||||
Given I have a local hosts file with:
|
||||
"""
|
||||
# <ip-address> <hostname.domain.org> <hostname>
|
||||
127.0.0.1 localhost.localdomain localhost
|
||||
"""
|
||||
When I run `middleman server --verbose --server-name localhost --bind-address 127.0.0.1` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to "127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://localhost:4567", "http://127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://localhost:4567/__middleman", "http://127.0.0.1:4567/__middleman"
|
||||
"""
|
||||
|
||||
Scenario: Start the server with server name "127.0.0.1"
|
||||
When I run `middleman server --verbose --server-name 127.0.0.1` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to "127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://127.0.0.1:4567/__middleman"
|
||||
"""
|
||||
|
||||
Scenario: Start the server with server name "::1"
|
||||
When I run `middleman server --verbose --server-name ::1` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to "::1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://[::1]:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://[::1]:4567/__middleman"
|
||||
"""
|
||||
|
||||
Scenario: Start the server with https
|
||||
When I run `middleman server --verbose --https` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to ":::4567", "0.0.0.0:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "https://
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "https://
|
||||
"""
|
||||
|
||||
Scenario: Start the server with port 65432
|
||||
When I run `middleman server --verbose --port 65432` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to ":::65432", "0.0.0.0:65432"
|
||||
"""
|
||||
|
||||
Scenario: Start the server with port 65432 configured via config.rb
|
||||
Given a file named "config.rb" with:
|
||||
"""
|
||||
set :port, 65432
|
||||
"""
|
||||
When I run `middleman server --verbose` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to ":::65432", "0.0.0.0:65432"
|
||||
"""
|
||||
|
||||
Scenario: Start the server when port is blocked by other middleman instance
|
||||
Given `middleman server` is running in background
|
||||
When I run `middleman server --verbose` interactively
|
||||
And I stop all commands if the output of the last command contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman uses a different port
|
||||
"""
|
||||
|
||||
Scenario: Start the server with bind address 1.1.1.1
|
||||
|
||||
This should fail, because "1.1.1.1" is not an interface available on this computer.
|
||||
|
||||
Given a file named ".hosts" with:
|
||||
"""
|
||||
1.1.1.1 www.example.com www
|
||||
"""
|
||||
When I run `middleman server --verbose --bind-address 1.1.1.1` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Running Middleman failed:
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
Bind address "1.1.1.1" is not available on your system
|
||||
"""
|
||||
|
||||
Scenario: Start the server with server name www.example.com and bind address 0.0.0.0
|
||||
|
||||
This should fail, because the user can just use `--server-name`. It does
|
||||
not make sense for `middleman` to only listen on `0.0.0.0` (IPv4 all
|
||||
interfaces), but not on `::` (IPv6 all interfaces). There are other tools
|
||||
like `iptables` (Linux-only) or better some `kernel`-configurations to make
|
||||
this possible.
|
||||
|
||||
When I run `middleman server --verbose --server-name www.example.com --bind-address 0.0.0.0` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Running Middleman failed:
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
Undefined combination of options "--server-name" and "--bind-address".
|
||||
"""
|
||||
|
||||
Scenario: Start the server with server name "www.example.com" and bind address "127.0.0.1"
|
||||
|
||||
This should fail because the server name does not resolve to the ip address.
|
||||
|
||||
Given a file named ".hosts" with:
|
||||
"""
|
||||
1.1.1.1 www.example.com www
|
||||
"""
|
||||
When I run `middleman server --verbose --server-name www.example.com --bind-address 127.0.0.1` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Running Middleman failed:
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
Server name "www.example.com" does not resolve to bind address "127.0.0.1". Please fix that and try again.
|
||||
"""
|
||||
|
||||
Scenario: Start the server with server name "garbage.example.com"
|
||||
When I run `middleman server --verbose --server-name garbage.example.com` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Running Middleman failed:
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
Server name "garbage.example.com" does not resolve to an ip address. Please fix that and try again.
|
||||
"""
|
||||
|
||||
Scenario: Start the server with server name "www.example.com" and the network name server is used to resolve the server name
|
||||
Given I have a local hosts file with:
|
||||
"""
|
||||
# empty
|
||||
"""
|
||||
And I start a mdns server with:
|
||||
"""
|
||||
# empty
|
||||
"""
|
||||
And I start a dns server with:
|
||||
"""
|
||||
www.example.com: 127.0.0.1
|
||||
"""
|
||||
When I run `middleman server --verbose --server-name www.example.com` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to "127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://www.example.com:4567", "http://127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://www.example.com:4567/__middleman", "http://127.0.0.1:4567/__middleman"
|
||||
"""
|
||||
|
||||
@ruby-2.1
|
||||
Scenario: Start the server with server name "host.local" and the link local name server is used to resolve the server name
|
||||
|
||||
To make the mdns resolver resolve a name, it needs to end with ".local".
|
||||
Otherwise the resolver returns [].
|
||||
|
||||
Given I have a local hosts file with:
|
||||
"""
|
||||
# empty
|
||||
"""
|
||||
And I start a mdns server with:
|
||||
"""
|
||||
host.local: 127.0.0.1
|
||||
"""
|
||||
When I run `middleman server --verbose --server-name host.local` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to "127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://host.local:4567", "http://127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://host.local:4567/__middleman", "http://127.0.0.1:4567/__middleman"
|
||||
"""
|
||||
|
||||
@ruby-2.1
|
||||
Scenario: Start the server with server name "host" and the link local name server is used to resolve the server name
|
||||
|
||||
To make the mdns resolver resolve a name, it needs to end with ".local". If
|
||||
a plain hostname is given `middleman` appends ".local" automatically.
|
||||
|
||||
Given I have a local hosts file with:
|
||||
"""
|
||||
# empty
|
||||
"""
|
||||
And I start a mdns server with:
|
||||
"""
|
||||
host.local: 127.0.0.1
|
||||
"""
|
||||
When I run `middleman server --verbose --server-name host` interactively
|
||||
And I stop middleman if the output contains:
|
||||
"""
|
||||
Inspect your site configuration
|
||||
"""
|
||||
Then the output should contain:
|
||||
"""
|
||||
The Middleman preview server is bound to "127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
View your site at "http://host.local:4567", "http://127.0.0.1:4567"
|
||||
"""
|
||||
And the output should contain:
|
||||
"""
|
||||
Inspect your site configuration at "http://host.local:4567/__middleman", "http://127.0.0.1:4567/__middleman"
|
||||
"""
|
|
@ -12,6 +12,8 @@ Feature: Setting the right content type for files
|
|||
Then the content type should be "text/css"
|
||||
When I go to "/README"
|
||||
Then the content type should be "text/plain"
|
||||
When I go to "/index.php"
|
||||
Then the content type should be "text/php"
|
||||
|
||||
Scenario: Content type can be set explicitly via page or proxy or frontmatter
|
||||
Given a fixture app "content-type-app"
|
||||
|
@ -31,6 +33,7 @@ Feature: Setting the right content type for files
|
|||
When I go to "/override.html"
|
||||
Then the content type should be "text/neato"
|
||||
|
||||
@preserve_mime_types
|
||||
Scenario: Content types can be overridden with mime_type
|
||||
Given a fixture app "content-type-app"
|
||||
And a file named "config.rb" with:
|
||||
|
|
209
middleman-core/features/i18n_link_to.feature
Normal file
209
middleman-core/features/i18n_link_to.feature
Normal file
|
@ -0,0 +1,209 @@
|
|||
Feature: i18n Paths
|
||||
|
||||
Scenario: link_to is i18n aware
|
||||
Given a fixture app "empty-app"
|
||||
And a file named "data/pages.yml" with:
|
||||
"""
|
||||
- hello.html
|
||||
"""
|
||||
And a file named "locales/en.yml" with:
|
||||
"""
|
||||
---
|
||||
en:
|
||||
msg: Hello
|
||||
home: Home
|
||||
"""
|
||||
And a file named "locales/es.yml" with:
|
||||
"""
|
||||
---
|
||||
es:
|
||||
paths:
|
||||
hello: "hola"
|
||||
msg: Hola
|
||||
home: Casa
|
||||
"""
|
||||
And a file named "source/localizable/index.html.erb" with:
|
||||
"""
|
||||
Page: <%= t(:hom) %>
|
||||
"""
|
||||
And a file named "source/localizable/hello.html.erb" with:
|
||||
"""
|
||||
Page: <%= t(:msg) %>
|
||||
|
||||
<%= link_to "Current Home", "/index.html", class: 'current' %>
|
||||
<%= link_to "Other Home", "/index.html", title: "Other Home", locale: ::I18n.locale == :en ? :es : :en %>
|
||||
<% link_to "/index.html", class: 'current' do %><span>Home: Current Block</span><% end %>
|
||||
<% link_to "/index.html", title: "Other Home", locale: ::I18n.locale == :en ? :es : :en do %><span>Home: Other Block</span><% end %>
|
||||
|
||||
<% data.pages.each_with_index do |p, i| %>
|
||||
<%= link_to "Current #{p}", "/#{p}", class: 'current' %>
|
||||
<%= link_to "Other #{p}", "/#{p}", title: "Other #{p}", locale: ::I18n.locale == :en ? :es : :en %>
|
||||
<% link_to "/#{p}", class: 'current' do %><span>Current Block</span><% end %>
|
||||
<% link_to "/#{p}", title: "Other #{p}", locale: ::I18n.locale == :en ? :es : :en do %><span>Other Block</span><% end %>
|
||||
<% end %>
|
||||
"""
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
set :strip_index_file, false
|
||||
activate :i18n, mount_at_root: :en
|
||||
"""
|
||||
Given the Server is running at "empty-app"
|
||||
When I go to "/hello.html"
|
||||
Then I should see "Page: Hello"
|
||||
Then I should see '<a class="current" href="/index.html">Current Home</a>'
|
||||
Then I should see '<a title="Other Home" href="/es/index.html">Other Home</a>'
|
||||
Then I should see '<a class="current" href="/index.html"><span>Home: Current Block</span></a>'
|
||||
Then I should see '<a title="Other Home" href="/es/index.html"><span>Home: Other Block</span></a>'
|
||||
Then I should see '<a class="current" href="/hello.html">Current hello.html</a>'
|
||||
Then I should see '<a title="Other hello.html" href="/es/hola.html">Other hello.html</a>'
|
||||
Then I should see '<a class="current" href="/hello.html"><span>Current Block</span></a>'
|
||||
Then I should see '<a title="Other hello.html" href="/es/hola.html"><span>Other Block</span></a>'
|
||||
When I go to "/es/hola.html"
|
||||
Then I should see "Page: Hola"
|
||||
Then I should see '<a class="current" href="/es/index.html">Current Home</a>'
|
||||
Then I should see '<a title="Other Home" href="/index.html">Other Home</a>'
|
||||
Then I should see '<a class="current" href="/es/index.html"><span>Home: Current Block</span></a>'
|
||||
Then I should see '<a title="Other Home" href="/index.html"><span>Home: Other Block</span></a>'
|
||||
Then I should see '<a class="current" href="/es/hola.html">Current hello.html</a>'
|
||||
Then I should see '<a title="Other hello.html" href="/hello.html">Other hello.html</a>'
|
||||
Then I should see '<a class="current" href="/es/hola.html"><span>Current Block</span></a>'
|
||||
Then I should see '<a title="Other hello.html" href="/hello.html"><span>Other Block</span></a>'
|
||||
|
||||
Scenario: link_to is i18n aware and supports relative_links
|
||||
Given a fixture app "empty-app"
|
||||
And a file named "locales/en.yml" with:
|
||||
"""
|
||||
---
|
||||
en:
|
||||
msg: Hello
|
||||
home: Home
|
||||
"""
|
||||
And a file named "locales/es.yml" with:
|
||||
"""
|
||||
---
|
||||
es:
|
||||
paths:
|
||||
hello: "hola"
|
||||
msg: Hola
|
||||
home: Casa
|
||||
"""
|
||||
And a file named "source/assets/css/main.css.scss" with:
|
||||
"""
|
||||
$color: red;
|
||||
body { background: $color; }
|
||||
"""
|
||||
And a file named "source/localizable/index.html.erb" with:
|
||||
"""
|
||||
Page: <%= t(:home) %>
|
||||
<%= stylesheet_link_tag :main %>
|
||||
"""
|
||||
And a file named "source/localizable/hello.html.erb" with:
|
||||
"""
|
||||
Page: <%= t(:msg) %>
|
||||
|
||||
<%= link_to "Current Home", "/index.html", class: 'current' %>
|
||||
<%= link_to "Other Home", "/index.html", title: "Other Home", locale: ::I18n.locale == :en ? :es : :en %>
|
||||
<% link_to "/index.html", class: 'current' do %><span>Home: Current Block</span><% end %>
|
||||
<% link_to "/index.html", title: "Other Home", locale: ::I18n.locale == :en ? :es : :en do %><span>Home: Other Block</span><% end %>
|
||||
|
||||
<%= link_to "Current hello.html", "/hello.html", class: 'current' %>
|
||||
<%= link_to "Other hello.html", "/hello.html", title: "Other hello.html", locale: ::I18n.locale == :en ? :es : :en %>
|
||||
<% link_to "/hello.html", class: 'current' do %><span>Current Block</span><% end %>
|
||||
<% link_to "/hello.html", title: "Other hello.html", locale: ::I18n.locale == :en ? :es : :en do %><span>Other Block</span><% end %>
|
||||
"""
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
set :css_dir, 'assets/css'
|
||||
set :relative_links, true
|
||||
set :strip_index_file, false
|
||||
activate :i18n, mount_at_root: :en
|
||||
activate :relative_assets
|
||||
"""
|
||||
Given the Server is running at "empty-app"
|
||||
When I go to "/index.html"
|
||||
Then I should see "assets/css/main.css"
|
||||
When I go to "/hello.html"
|
||||
Then I should see "Page: Hello"
|
||||
Then I should see '<a class="current" href="index.html">Current Home</a>'
|
||||
Then I should see '<a title="Other Home" href="es/index.html">Other Home</a>'
|
||||
Then I should see '<a class="current" href="index.html"><span>Home: Current Block</span></a>'
|
||||
Then I should see '<a title="Other Home" href="es/index.html"><span>Home: Other Block</span></a>'
|
||||
Then I should see '<a class="current" href="hello.html">Current hello.html</a>'
|
||||
Then I should see '<a title="Other hello.html" href="es/hola.html">Other hello.html</a>'
|
||||
Then I should see '<a class="current" href="hello.html"><span>Current Block</span></a>'
|
||||
Then I should see '<a title="Other hello.html" href="es/hola.html"><span>Other Block</span></a>'
|
||||
When I go to "/es/hola.html"
|
||||
Then I should see "Page: Hola"
|
||||
Then I should see '<a class="current" href="index.html">Current Home</a>'
|
||||
Then I should see '<a title="Other Home" href="../index.html">Other Home</a>'
|
||||
Then I should see '<a class="current" href="index.html"><span>Home: Current Block</span></a>'
|
||||
Then I should see '<a title="Other Home" href="../index.html"><span>Home: Other Block</span></a>'
|
||||
Then I should see '<a class="current" href="hola.html">Current hello.html</a>'
|
||||
Then I should see '<a title="Other hello.html" href="../hello.html">Other hello.html</a>'
|
||||
Then I should see '<a class="current" href="hola.html"><span>Current Block</span></a>'
|
||||
Then I should see '<a title="Other hello.html" href="../hello.html"><span>Other Block</span></a>'
|
||||
|
||||
Scenario: url_for is i18n aware
|
||||
Given a fixture app "empty-app"
|
||||
And a file named "data/pages.yml" with:
|
||||
"""
|
||||
- hello.html
|
||||
- article.html
|
||||
"""
|
||||
And a file named "locales/en.yml" with:
|
||||
"""
|
||||
---
|
||||
en:
|
||||
msg: Hello
|
||||
"""
|
||||
And a file named "locales/es.yml" with:
|
||||
"""
|
||||
---
|
||||
es:
|
||||
paths:
|
||||
hello: "hola"
|
||||
msg: Hola
|
||||
"""
|
||||
And a file named "source/localizable/hello.html.erb" with:
|
||||
"""
|
||||
Page: <%= t(:msg) %>
|
||||
<% data.pages.each_with_index do |p, i| %>
|
||||
Current: <%= url_for "/#{p}" %>
|
||||
Other: <%= url_for "/#{p}", locale: ::I18n.locale == :en ? :es : :en %>
|
||||
<% end %>
|
||||
"""
|
||||
And a file named "source/localizable/article.html.erb" with:
|
||||
"""
|
||||
Page Lang: Default
|
||||
|
||||
Current: <%= url_for "/article.html" %>
|
||||
Other: <%= url_for "/article.html", locale: ::I18n.locale == :en ? :es : :en %>
|
||||
"""
|
||||
And a file named "source/localizable/article.es.html.erb" with:
|
||||
"""
|
||||
Page Lang: Spanish
|
||||
|
||||
Current: <%= url_for "/article.html" %>
|
||||
Other: <%= url_for "/article.html", locale: :en %>
|
||||
"""
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
activate :i18n, mount_at_root: :en
|
||||
"""
|
||||
Given the Server is running at "empty-app"
|
||||
When I go to "/hello.html"
|
||||
Then I should see "Page: Hello"
|
||||
Then I should see 'Current: /hello.html'
|
||||
Then I should see 'Other: /es/hola.html'
|
||||
When I go to "/es/hola.html"
|
||||
Then I should see "Page: Hola"
|
||||
Then I should see 'Current: /es/hola.html'
|
||||
Then I should see 'Other: /hello.html'
|
||||
When I go to "/article.html"
|
||||
Then I should see "Page Lang: Default"
|
||||
Then I should see 'Current: /article.html'
|
||||
Then I should see 'Other: /es/article.html'
|
||||
When I go to "/es/article.html"
|
||||
Then I should see "Page Lang: Spanish"
|
||||
Then I should see 'Current: /es/article.html'
|
||||
Then I should see 'Other: /article.html'
|
18
middleman-core/features/javascript-testing.feature
Normal file
18
middleman-core/features/javascript-testing.feature
Normal file
|
@ -0,0 +1,18 @@
|
|||
Feature: Test a site with javascript included
|
||||
|
||||
As a software developer
|
||||
I want to develop a site using javascript
|
||||
I would like to have a server step rendering javascript correctly in order to test it
|
||||
|
||||
@javascript
|
||||
Scenario: Existing app with javascript
|
||||
Given the Server is running at "javascript-app"
|
||||
When I go to "/index.html"
|
||||
Then I should see:
|
||||
"""
|
||||
Local Hour
|
||||
"""
|
||||
And I should see:
|
||||
"""
|
||||
Local Minutes
|
||||
"""
|
|
@ -1,4 +1,3 @@
|
|||
@nojava
|
||||
Feature: Markdown (Redcarpet) support
|
||||
In order to test included Redcarpet support
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
@nojava
|
||||
Feature: Markdown support in Haml
|
||||
In order to test support of the Haml markdown filter
|
||||
|
||||
|
|
|
@ -26,6 +26,18 @@ Feature: Minify CSS
|
|||
When I go to "/stylesheets/report.css"
|
||||
Then I should see "p{border:1px solid #ff6600}"
|
||||
|
||||
Scenario: Rendering external css in a proxied resource
|
||||
Given a fixture app "minify-css-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
activate :minify_css
|
||||
proxy '/css-proxy', '/stylesheets/site.css', ignore: true
|
||||
"""
|
||||
And the Server is running at "minify-css-app"
|
||||
When I go to "/css-proxy"
|
||||
Then I should see "1" lines
|
||||
And I should see "only screen and (device-width"
|
||||
|
||||
Scenario: Rendering external css with passthrough compressor
|
||||
Given a fixture app "passthrough-app"
|
||||
And a file named "config.rb" with:
|
||||
|
@ -120,4 +132,54 @@ Feature: Minify CSS
|
|||
<style>
|
||||
body{test:style;good:deal}
|
||||
</style>
|
||||
"""
|
||||
"""
|
||||
|
||||
Scenario: Rendering inline css in a PHP document
|
||||
Given a fixture app "minify-css-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
activate :minify_css, :inline => true
|
||||
"""
|
||||
And the Server is running at "minify-css-app"
|
||||
When I go to "/inline-css.php"
|
||||
Then I should see:
|
||||
"""
|
||||
<?='Hello'?>
|
||||
|
||||
<style>
|
||||
body{test:style;good:deal}
|
||||
</style>
|
||||
"""
|
||||
|
||||
Scenario: Rendering inline css in a proxied resource
|
||||
Given a fixture app "minify-css-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
activate :minify_css, :inline => true
|
||||
proxy '/inline-css-proxy', '/inline-css.html', ignore: true
|
||||
"""
|
||||
And the Server is running at "minify-css-app"
|
||||
When I go to "/inline-css-proxy"
|
||||
Then I should see:
|
||||
"""
|
||||
<style>
|
||||
body{test:style;good:deal}
|
||||
</style>
|
||||
"""
|
||||
|
||||
@preserve_mime_types
|
||||
Scenario: Configuring content types of resources to be minified
|
||||
Given a fixture app "minify-css-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
mime_type('.xcss', 'text/x-css')
|
||||
activate :minify_css, content_types: ['text/x-css'],
|
||||
inline: true,
|
||||
inline_content_types: ['text/html']
|
||||
"""
|
||||
And the Server is running at "minify-css-app"
|
||||
When I go to "/stylesheets/site.xcss"
|
||||
Then I should see "1" lines
|
||||
And I should see "only screen and (device-width"
|
||||
When I go to "/inline-css.php"
|
||||
Then I should see "8" lines
|
||||
|
|
|
@ -86,7 +86,7 @@ Feature: Minify Javascript
|
|||
</script>
|
||||
"""
|
||||
|
||||
Scenario: Rendering inline css with a passthrough minifier using activate-style compressor
|
||||
Scenario: Rendering inline JS with a passthrough minifier using activate-style compressor
|
||||
Given a fixture app "passthrough-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
|
@ -146,6 +146,42 @@ Feature: Minify Javascript
|
|||
</script>
|
||||
"""
|
||||
|
||||
Scenario: Rendering inline js in a PHP document
|
||||
Given a fixture app "minify-js-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
activate :minify_javascript, :inline => true
|
||||
"""
|
||||
And the Server is running at "minify-js-app"
|
||||
When I go to "/inline-js.php"
|
||||
Then I should see:
|
||||
"""
|
||||
<?='Hello'?>
|
||||
|
||||
<script>
|
||||
!function(){should(),all.be(),on={one:line}}();
|
||||
</script>
|
||||
<script type='text/javascript'>
|
||||
//<!--
|
||||
!function(){one,line(),here()}();
|
||||
//-->
|
||||
</script>
|
||||
<script type='text/html'>
|
||||
I'm a jQuery {{template}}.
|
||||
</script>
|
||||
"""
|
||||
|
||||
Scenario: Rendering inline js in a proxied resource
|
||||
Given a fixture app "minify-js-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
activate :minify_javascript, :inline => true
|
||||
proxy '/inline-js-proxy', '/inline-js.html', ignore: true
|
||||
"""
|
||||
And the Server is running at "minify-js-app"
|
||||
When I go to "/inline-js-proxy"
|
||||
Then I should see "14" lines
|
||||
|
||||
Scenario: Rendering external js with the feature enabled
|
||||
Given a fixture app "minify-js-app"
|
||||
And a file named "config.rb" with:
|
||||
|
@ -158,6 +194,17 @@ Feature: Minify Javascript
|
|||
When I go to "/more-js/other.js"
|
||||
Then I should see "1" lines
|
||||
|
||||
Scenario: Rendering external js in a proxied resource
|
||||
Given a fixture app "minify-js-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
activate :minify_javascript
|
||||
proxy '/js-proxy', '/javascripts/js_test.js', ignore: true
|
||||
"""
|
||||
And the Server is running at "minify-js-app"
|
||||
When I go to "/js-proxy"
|
||||
Then I should see "1" lines
|
||||
|
||||
Scenario: Rendering external js with a passthrough minifier
|
||||
And the Server is running at "passthrough-app"
|
||||
When I go to "/javascripts/js_test.js"
|
||||
|
|
|
@ -8,6 +8,7 @@ Feature: Meta redirects
|
|||
"""
|
||||
And the Server is running at "large-build-app"
|
||||
When I go to "/hello.html"
|
||||
Then I should see '<link rel="canonical" href="world.html"'
|
||||
Then I should see '<meta http-equiv=refresh content="0; url=world.html"'
|
||||
|
||||
Scenario: Redirect to external site
|
||||
|
|
|
@ -21,6 +21,12 @@ Feature: Relative Assets
|
|||
Given "relative_assets" feature is "disabled"
|
||||
And the Server is running at "relative-assets-app"
|
||||
When I go to "/relative_image.html"
|
||||
Then I should see '"/stylesheets/relative_assets.css"'
|
||||
Then I should see '"/javascripts/app.js"'
|
||||
Then I should see "/images/blank.gif"
|
||||
When I go to "/absolute_image_relative_css.html"
|
||||
Then I should see '"stylesheets/relative_assets.css"'
|
||||
Then I should see '"javascripts/app.js"'
|
||||
Then I should see "/images/blank.gif"
|
||||
|
||||
Scenario: Rendering css with the feature enabled
|
||||
|
@ -57,6 +63,11 @@ Feature: Relative Assets
|
|||
Given "relative_assets" feature is "enabled"
|
||||
And the Server is running at "relative-assets-app"
|
||||
When I go to "/relative_image.html"
|
||||
Then I should see '"stylesheets/relative_assets.css"'
|
||||
Then I should see '"javascripts/app.js"'
|
||||
When I go to "/relative_image_absolute_css.html"
|
||||
Then I should see '"/stylesheets/relative_assets.css"'
|
||||
Then I should see '"/javascripts/app.js"'
|
||||
Then I should not see "/images/blank.gif"
|
||||
And I should see "images/blank.gif"
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
@nojava
|
||||
Feature: Stylus Updates and Partials
|
||||
Scenario: The preview server should update stylesheets when Stylus changes
|
||||
Given the Server is running at "stylus-preview-app"
|
||||
|
|
|
@ -4,6 +4,9 @@ ENV["AUTOLOAD_SPROCKETS"] ||= "false"
|
|||
require 'simplecov'
|
||||
SimpleCov.root(File.expand_path(File.dirname(__FILE__) + '/../..'))
|
||||
|
||||
require 'capybara/poltergeist'
|
||||
Capybara.javascript_driver = :poltergeist
|
||||
|
||||
require 'coveralls'
|
||||
Coveralls.wear!
|
||||
|
||||
|
|
7
middleman-core/features/support/preserve_mime_types.rb
Normal file
7
middleman-core/features/support/preserve_mime_types.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
Around('@preserve_mime_types') do |_scenario, block|
|
||||
mime_types = ::Rack::Mime::MIME_TYPES.clone
|
||||
|
||||
block.call
|
||||
|
||||
::Rack::Mime::MIME_TYPES.replace mime_types
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
@nojava @nowindows
|
||||
@nowindows
|
||||
Feature: Compile a complicated Twitter bootstrap app
|
||||
|
||||
Scenario: User drops Twitter Bootstrap source into an app
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
#main {
|
||||
padding: 50px;
|
||||
background-image: image-url('100px.jpg');
|
||||
}
|
||||
background-image: url('/images/100px.jpg');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
@font-face {
|
||||
font-family: 'FontAwesome';
|
||||
src: url('../fonts/fontawesome-webfont.woff2') format('woff2'), url('../fonts/fontawesome-webfont.woff') format('woff');
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<?="I'm a PHP file!"?>
|
0
middleman-core/fixtures/javascript-app/config.rb
Normal file
0
middleman-core/fixtures/javascript-app/config.rb
Normal file
17
middleman-core/fixtures/javascript-app/source/index.html
Normal file
17
middleman-core/fixtures/javascript-app/source/index.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Title
|
||||
</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript" language="JavaScript">
|
||||
<!--
|
||||
current_date = new Date();
|
||||
document.write('Now: ');
|
||||
document.write(current_date.getHours() + " Local H" + "our");
|
||||
document.write(current_date.getMinutes() + " Local M" + "inutes");
|
||||
//-->
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,8 @@
|
|||
<?='Hello'?>
|
||||
|
||||
<style>
|
||||
body {
|
||||
test: style;
|
||||
good: deal;
|
||||
}
|
||||
</style>
|
5
middleman-core/fixtures/minify-css-app/source/stylesheets/site.xcss.sass
Executable file
5
middleman-core/fixtures/minify-css-app/source/stylesheets/site.xcss.sass
Executable file
|
@ -0,0 +1,5 @@
|
|||
@import "compass/reset"
|
||||
|
||||
@media handheld, only screen and (device-width: 768px)
|
||||
body
|
||||
display: block
|
22
middleman-core/fixtures/minify-js-app/source/inline-js.php
Executable file
22
middleman-core/fixtures/minify-js-app/source/inline-js.php
Executable file
|
@ -0,0 +1,22 @@
|
|||
<?='Hello'?>
|
||||
|
||||
<script>
|
||||
;(function() {
|
||||
this;
|
||||
should();
|
||||
all.be();
|
||||
on = { one: line };
|
||||
})();
|
||||
</script>
|
||||
<script type='text/javascript'>
|
||||
//<!--
|
||||
;(function() {
|
||||
one;
|
||||
line();
|
||||
here();
|
||||
})();
|
||||
//-->
|
||||
</script>
|
||||
<script type='text/html'>
|
||||
I'm a jQuery {{template}}.
|
||||
</script>
|
|
@ -0,0 +1,8 @@
|
|||
var race;
|
||||
var __slice = Array.prototype.slice;
|
||||
|
||||
race = function() {
|
||||
var runners, winner;
|
||||
winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
||||
return print(winner, runners);
|
||||
};
|
33
middleman-core/fixtures/preview-server-app/bin/dns_server.rb
Executable file
33
middleman-core/fixtures/preview-server-app/bin/dns_server.rb
Executable file
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require 'rubydns'
|
||||
require 'psych'
|
||||
|
||||
db_file = ARGV[0]
|
||||
port = ARGV[1] || 5300
|
||||
|
||||
db = if File.file? db_file
|
||||
$stderr.puts 'Found dns db'
|
||||
Psych.load_file(db_file)
|
||||
else
|
||||
$stderr.puts 'Found no dns db. Use default db.'
|
||||
|
||||
{
|
||||
/www\.example\.org/ => '1.1.1.1'
|
||||
}
|
||||
end
|
||||
|
||||
interfaces = [
|
||||
[:udp, "127.0.0.1", port],
|
||||
[:tcp, "127.0.0.1", port]
|
||||
]
|
||||
|
||||
|
||||
# Start the RubyDNS server
|
||||
RubyDNS::run_server(:listen => interfaces) do
|
||||
db.each do |matcher, result|
|
||||
match(matcher, Resolv::DNS::Resource::IN::A) do |transaction|
|
||||
transaction.respond!(result)
|
||||
end
|
||||
end
|
||||
end
|
1
middleman-core/fixtures/preview-server-app/source/index.html.erb
Executable file
1
middleman-core/fixtures/preview-server-app/source/index.html.erb
Executable file
|
@ -0,0 +1 @@
|
|||
<h1>Welcome</h1>
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>My Sample Site</title>
|
||||
<!-- Comment in layout -->
|
||||
</head>
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
8
middleman-core/fixtures/preview-server-app/source/layouts/custom.erb
Executable file
8
middleman-core/fixtures/preview-server-app/source/layouts/custom.erb
Executable file
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Custom Layout</title>
|
||||
</head>
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
I am real
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
layout: false
|
||||
---
|
||||
|
||||
I am real: <%= @num %>
|
|
@ -0,0 +1 @@
|
|||
<h1>Ignore me!</h1>
|
|
@ -0,0 +1 @@
|
|||
<h1>Ignore me! 2</h1>
|
|
@ -0,0 +1 @@
|
|||
<h1>Ignore me! 3</h1>
|
1
middleman-core/fixtures/preview-server-app/source/static.html
Executable file
1
middleman-core/fixtures/preview-server-app/source/static.html
Executable file
|
@ -0,0 +1 @@
|
|||
Static, no code!
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<head>
|
||||
<%= stylesheet_link_tag :relative_assets, relative: true %>
|
||||
<%= javascript_include_tag :app, relative: true %>
|
||||
</head>
|
||||
<body>
|
||||
<%= image_tag "blank.gif" %>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,3 @@
|
|||
function hello() {
|
||||
console.log('world');
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<%= stylesheet_link_tag :relative_assets %>
|
||||
<%= javascript_include_tag :app %>
|
||||
</head>
|
||||
<body>
|
||||
<%= image_tag "blank.gif" %>
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<head>
|
||||
<%= stylesheet_link_tag :relative_assets, relative: false %>
|
||||
<%= javascript_include_tag :app, relative: false %>
|
||||
</head>
|
||||
<body>
|
||||
<%= image_tag "blank.gif" %>
|
||||
</body>
|
||||
</html>
|
|
@ -33,7 +33,9 @@ module Middleman
|
|||
# Root project directory (overwritten in middleman build/server)
|
||||
# @return [String]
|
||||
def root
|
||||
ENV['MM_ROOT'] || Dir.pwd
|
||||
r = ENV['MM_ROOT'] ? ENV['MM_ROOT'].dup : ::Middleman::Util.current_directory
|
||||
r.encode!('UTF-8', 'UTF-8-MAC') if RUBY_PLATFORM =~ /darwin/
|
||||
r
|
||||
end
|
||||
|
||||
# Pathname-addressed root
|
||||
|
@ -66,13 +68,17 @@ module Middleman
|
|||
Contract SetOf[MapDescriptor]
|
||||
attr_reader :mappings
|
||||
|
||||
# Which host preview should start on.
|
||||
# @return [Fixnum]
|
||||
define_setting :host, '0.0.0.0', 'The preview server host'
|
||||
|
||||
# Which port preview should start on.
|
||||
# @return [Fixnum]
|
||||
define_setting :port, 4567, 'The preview server port'
|
||||
config.define_setting :port, 4567, 'The preview server port'
|
||||
|
||||
# Which server name should be used
|
||||
# @return [NilClass, String]
|
||||
config.define_setting :server_name, nil, 'The server name of preview server'
|
||||
|
||||
# Which bind address the preview server should use
|
||||
# @return [NilClass, String]
|
||||
config.define_setting :bind_address, nil, 'The bind address of the preview server'
|
||||
|
||||
# Whether to serve the preview server over HTTPS.
|
||||
# @return [Boolean]
|
||||
|
@ -160,7 +166,7 @@ module Middleman
|
|||
# Setup callbacks which can exclude paths from the sitemap
|
||||
define_setting :ignored_sitemap_matchers, {
|
||||
# Files starting with an underscore, but not a double-underscore
|
||||
partials: proc { |file|
|
||||
partials: proc do |file|
|
||||
ignored = false
|
||||
|
||||
file[:relative_path].ascend do |f|
|
||||
|
@ -171,12 +177,12 @@ module Middleman
|
|||
end
|
||||
|
||||
ignored
|
||||
},
|
||||
end,
|
||||
|
||||
layout: proc { |file, _sitemap_app|
|
||||
layout: proc do |file, _sitemap_app|
|
||||
file[:relative_path].to_s.start_with?('layout.') ||
|
||||
file[:relative_path].to_s.start_with?('layouts/')
|
||||
}
|
||||
end
|
||||
}, 'Callbacks that can exclude paths from the sitemap'
|
||||
|
||||
define_setting :watcher_disable, false, 'If the Listen watcher should not run'
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
# CLI Module
|
||||
module Middleman::Cli
|
||||
# Server thor task
|
||||
class Server < Thor
|
||||
check_unknown_options!
|
||||
|
||||
namespace :server
|
||||
|
||||
desc 'server [options]', 'Start the preview server'
|
||||
method_option :environment,
|
||||
aliases: '-e',
|
||||
default: ENV['MM_ENV'] || ENV['RACK_ENV'] || 'development',
|
||||
desc: 'The environment Middleman will run under'
|
||||
method_option :host,
|
||||
type: :string,
|
||||
aliases: '-h',
|
||||
desc: 'Bind to HOST address'
|
||||
method_option :port,
|
||||
aliases: '-p',
|
||||
desc: 'The port Middleman will listen on'
|
||||
method_option :https,
|
||||
type: :boolean,
|
||||
desc: 'Serve the preview server over SSL/TLS'
|
||||
method_option :ssl_certificate,
|
||||
desc: 'Path to an X.509 certificate to use for the preview server'
|
||||
method_option :ssl_private_key,
|
||||
desc: "Path to an RSA private key for the preview server's certificate"
|
||||
method_option :verbose,
|
||||
type: :boolean,
|
||||
default: false,
|
||||
desc: 'Print debug messages'
|
||||
method_option :instrument,
|
||||
type: :string,
|
||||
default: false,
|
||||
desc: 'Print instrument messages'
|
||||
method_option :disable_watcher,
|
||||
type: :boolean,
|
||||
default: false,
|
||||
desc: 'Disable the file change and delete watcher process'
|
||||
method_option :profile,
|
||||
type: :boolean,
|
||||
default: false,
|
||||
desc: 'Generate profiling report for server startup'
|
||||
method_option :reload_paths,
|
||||
type: :string,
|
||||
default: false,
|
||||
desc: 'Additional paths to auto-reload when files change'
|
||||
method_option :force_polling,
|
||||
type: :boolean,
|
||||
default: false,
|
||||
desc: 'Force file watcher into polling mode'
|
||||
method_option :latency,
|
||||
type: :numeric,
|
||||
aliases: '-l',
|
||||
default: 0.25,
|
||||
desc: 'Set file watcher latency, in seconds'
|
||||
|
||||
# Start the server
|
||||
def server
|
||||
require 'middleman-core'
|
||||
require 'middleman-core/preview_server'
|
||||
|
||||
unless ENV['MM_ROOT']
|
||||
puts '== Could not find a Middleman project config.rb'
|
||||
puts '== Treating directory as a static site to be served'
|
||||
ENV['MM_ROOT'] = Dir.pwd
|
||||
ENV['MM_SOURCE'] = ''
|
||||
end
|
||||
|
||||
params = {
|
||||
port: options['port'],
|
||||
host: options['host'],
|
||||
https: options['https'],
|
||||
ssl_certificate: options['ssl_certificate'],
|
||||
ssl_private_key: options['ssl_private_key'],
|
||||
environment: options['environment'],
|
||||
debug: options['verbose'],
|
||||
instrumenting: options['instrument'],
|
||||
disable_watcher: options['disable_watcher'],
|
||||
reload_paths: options['reload_paths'],
|
||||
force_polling: options['force_polling'],
|
||||
latency: options['latency']
|
||||
}
|
||||
|
||||
puts '== The Middleman is loading'
|
||||
::Middleman::PreviewServer.start(params)
|
||||
end
|
||||
end
|
||||
|
||||
def self.exit_on_failure?
|
||||
true
|
||||
end
|
||||
|
||||
# Map "s" to "server"
|
||||
Base.map('s' => 'server')
|
||||
end
|
|
@ -150,7 +150,7 @@ module Middleman
|
|||
|
||||
# Whether or not there has been a value set beyond the default
|
||||
def value_set?
|
||||
@value_set
|
||||
@value_set == true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -104,6 +104,36 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
|
|||
end
|
||||
end
|
||||
|
||||
# Override helper to add `relative` opt-out.
|
||||
def stylesheet_link_tag(*sources)
|
||||
options = {
|
||||
rel: 'stylesheet'
|
||||
}.update(sources.extract_options!.symbolize_keys)
|
||||
|
||||
path_options = {}
|
||||
path_options[:relative] = options.delete(:relative) if options.key?(:relative)
|
||||
|
||||
sources.flatten.inject(::ActiveSupport::SafeBuffer.new) do |all, source|
|
||||
all << tag(:link, {
|
||||
href: asset_path(:css, source, path_options)
|
||||
}.update(options))
|
||||
end
|
||||
end
|
||||
|
||||
# Override helper to add `relative` opt-out.
|
||||
def javascript_include_tag(*sources)
|
||||
options = sources.extract_options!.symbolize_keys
|
||||
|
||||
path_options = {}
|
||||
path_options[:relative] = options.delete(:relative) if options.key?(:relative)
|
||||
|
||||
sources.flatten.inject(::ActiveSupport::SafeBuffer.new) do |all, source|
|
||||
all << content_tag(:script, nil, {
|
||||
src: asset_path(:js, source, path_options)
|
||||
}.update(options))
|
||||
end
|
||||
end
|
||||
|
||||
# Output a stylesheet link tag based on the current path
|
||||
#
|
||||
# @param [Symbol] asset_ext The type of asset
|
||||
|
@ -161,16 +191,19 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
|
|||
# @param [Hash] options Data to pass through.
|
||||
# @return [String]
|
||||
def asset_path(kind, source, options={})
|
||||
::Middleman::Util.asset_path(app, kind, source, options)
|
||||
options_with_resource = options.merge(current_resource: current_resource)
|
||||
::Middleman::Util.asset_path(app, kind, source, options_with_resource)
|
||||
end
|
||||
|
||||
# Get the URL of an asset given a type/prefix
|
||||
#
|
||||
# @param [String] path The path (such as "photo.jpg")
|
||||
# @param [String] prefix The type prefix (such as "images")
|
||||
# @param [Hash] options Additional options.
|
||||
# @return [String] The fully qualified asset url
|
||||
def asset_url(_path, prefix='', options={})
|
||||
::Middleman::Util.asset_url(app, prefix, options)
|
||||
options_with_resource = options.merge(current_resource: current_resource)
|
||||
::Middleman::Util.asset_url(app, prefix, options_with_resource)
|
||||
end
|
||||
|
||||
# Given a source path (referenced either absolutely or relatively)
|
||||
|
|
|
@ -19,7 +19,8 @@ module Middleman
|
|||
|
||||
return unless File.exist?(helpers_path)
|
||||
|
||||
Dir[File.join(helpers_path, app.config[:helpers_filename_glob])].each do |filename|
|
||||
glob = File.join(helpers_path, app.config[:helpers_filename_glob])
|
||||
::Middleman::Util.glob_directory(glob).each do |filename|
|
||||
module_name = app.config[:helpers_filename_to_module_name_proc].call(filename)
|
||||
next unless module_name
|
||||
|
||||
|
|
|
@ -63,6 +63,33 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
|
|||
::I18n.t(*args)
|
||||
end
|
||||
|
||||
def url_for(path_or_resource, options={})
|
||||
locale = options.delete(:locale) || ::I18n.locale
|
||||
|
||||
opts = options.dup
|
||||
|
||||
should_relativize = opts.key?(:relative) ? opts[:relative] : config[:relative_links]
|
||||
|
||||
opts[:relative] = false
|
||||
|
||||
href = super(path_or_resource, opts)
|
||||
|
||||
final_path = if result = extensions[:i18n].localized_path(href, locale)
|
||||
result
|
||||
else
|
||||
# Should we log the missing file?
|
||||
href
|
||||
end
|
||||
|
||||
opts[:relative] = should_relativize
|
||||
|
||||
begin
|
||||
super(final_path, opts)
|
||||
rescue RuntimeError
|
||||
super(path_or_resource, options)
|
||||
end
|
||||
end
|
||||
|
||||
def locate_partial(partial_name, try_static=false)
|
||||
locals_dir = extensions[:i18n].options[:templates_dir]
|
||||
|
||||
|
@ -102,19 +129,21 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
|
|||
def manipulate_resource_list(resources)
|
||||
new_resources = []
|
||||
|
||||
resources.each do |resource|
|
||||
# If it uses file extension localization
|
||||
if result = parse_locale_extension(resource.path)
|
||||
ext_lang, path, page_id = result
|
||||
new_resources << build_resource(path, resource.path, page_id, ext_lang)
|
||||
# If it's a "localizable template"
|
||||
elsif File.fnmatch?(File.join(options[:templates_dir], '**'), resource.path)
|
||||
page_id = File.basename(resource.path, File.extname(resource.path))
|
||||
langs.each do |lang|
|
||||
# Remove folder name
|
||||
path = resource.path.sub(options[:templates_dir], '')
|
||||
new_resources << build_resource(path, resource.path, page_id, lang)
|
||||
end
|
||||
file_extension_resources = resources.select do |resource|
|
||||
parse_locale_extension(resource.path)
|
||||
end
|
||||
|
||||
localizable_folder_resources = resources.select do |resource|
|
||||
!file_extension_resources.include?(resource) && File.fnmatch?(File.join(options[:templates_dir], '**'), resource.path)
|
||||
end
|
||||
|
||||
# If it's a "localizable template"
|
||||
localizable_folder_resources.map do |resource|
|
||||
page_id = File.basename(resource.path, File.extname(resource.path))
|
||||
langs.each do |lang|
|
||||
# Remove folder name
|
||||
path = resource.path.sub(options[:templates_dir], '')
|
||||
new_resources << build_resource(path, resource.path, page_id, lang)
|
||||
end
|
||||
|
||||
# This is for backwards compatibility with the old provides_metadata-based code
|
||||
|
@ -124,7 +153,27 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
|
|||
resource.add_metadata options: { lang: @mount_at_root }, locals: { lang: @mount_at_root }
|
||||
end
|
||||
|
||||
resources + new_resources
|
||||
# If it uses file extension localization
|
||||
file_extension_resources.map do |resource|
|
||||
result = parse_locale_extension(resource.path)
|
||||
ext_lang, path, page_id = result
|
||||
new_resources << build_resource(path, resource.path, page_id, ext_lang)
|
||||
end
|
||||
|
||||
@lookup = new_resources.each_with_object({}) do |desc, sum|
|
||||
abs_path = desc.source_path.sub(options[:templates_dir], '')
|
||||
sum[abs_path] ||= {}
|
||||
sum[abs_path][desc.lang] = '/' + desc.path
|
||||
end
|
||||
|
||||
resources + new_resources.map { |r| r.to_resource(app) }
|
||||
end
|
||||
|
||||
def localized_path(path, lang)
|
||||
lookup_path = path.dup
|
||||
lookup_path << app.config[:index_file] if lookup_path.end_with?('/')
|
||||
|
||||
@lookup[lookup_path] && @lookup[lookup_path][lang]
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -155,9 +204,9 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
|
|||
p[:relative_path].to_s.split(File::SEPARATOR).length == 1
|
||||
end
|
||||
|
||||
known_langs.map { |p|
|
||||
known_langs.map do |p|
|
||||
File.basename(p[:relative_path].to_s).sub(/\.ya?ml$/, '').sub(/\.rb$/, '')
|
||||
}.sort.map(&:to_sym)
|
||||
end.sort.map(&:to_sym)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -177,12 +226,29 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
|
|||
[lang, path, basename]
|
||||
end
|
||||
|
||||
Contract String, String, String, Symbol => IsA['Middleman::Sitemap::Resource']
|
||||
LocalizedPageDescriptor = Struct.new(:path, :source_path, :lang) do
|
||||
def to_resource(app)
|
||||
r = ::Middleman::Sitemap::ProxyResource.new(app.sitemap, path, source_path)
|
||||
r.add_metadata options: { lang: lang }
|
||||
r
|
||||
end
|
||||
end
|
||||
|
||||
Contract String, String, String, Symbol => LocalizedPageDescriptor
|
||||
def build_resource(path, source_path, page_id, lang)
|
||||
old_locale = ::I18n.locale
|
||||
::I18n.locale = lang
|
||||
localized_page_id = ::I18n.t("paths.#{page_id}", default: page_id, fallback: [])
|
||||
|
||||
partially_localized_path = ''
|
||||
|
||||
File.dirname(path).split('/').each do |path_sub|
|
||||
next if path_sub == ''
|
||||
partially_localized_path = "#{partially_localized_path}/#{(::I18n.t("paths.#{path_sub}", default: path_sub).to_s)}"
|
||||
end
|
||||
|
||||
path = "#{partially_localized_path}/#{File.basename(path)}"
|
||||
|
||||
prefix = if (options[:mount_at_root] == lang) || (options[:mount_at_root].nil? && langs[0] == lang)
|
||||
'/'
|
||||
else
|
||||
|
@ -197,10 +263,8 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
|
|||
|
||||
path = path.sub(options[:templates_dir] + '/', '')
|
||||
|
||||
p = ::Middleman::Sitemap::ProxyResource.new(app.sitemap, path, source_path)
|
||||
p.add_metadata locals: { lang: lang, page_id: path }, options: { lang: lang }
|
||||
|
||||
::I18n.locale = old_locale
|
||||
p
|
||||
|
||||
LocalizedPageDescriptor.new(path, source_path, lang)
|
||||
end
|
||||
end
|
||||
|
|
73
middleman-core/lib/middleman-core/dns_resolver.rb
Normal file
73
middleman-core/lib/middleman-core/dns_resolver.rb
Normal file
|
@ -0,0 +1,73 @@
|
|||
require 'resolv'
|
||||
require 'middleman-core/dns_resolver/network_resolver'
|
||||
require 'middleman-core/dns_resolver/hosts_resolver'
|
||||
|
||||
module Middleman
|
||||
# This resolves IP address to names and vice versa
|
||||
class DnsResolver
|
||||
private
|
||||
|
||||
attr_reader :resolvers
|
||||
|
||||
public
|
||||
|
||||
# Create resolver
|
||||
#
|
||||
# First the local resolver is used. If environment variable HOSTSRC is
|
||||
# given this file is used for local name lookup.
|
||||
#
|
||||
# @param [#getnames, #getaddresses] network_resolver
|
||||
# The resolver which uses a network name server to resolve ip addresses
|
||||
# and names.
|
||||
#
|
||||
# @param [#getnames, #getaddresses] local_resolver
|
||||
# The resolver uses /etc/hosts on POSIX-systems and
|
||||
# C:\Windows\System32\drivers\etc\hosts on Windows-operating systems to
|
||||
# resolve ip addresses and names.
|
||||
#
|
||||
# First the local resolver is queried. If this raises an error or returns
|
||||
# nil or [] the network resolver is queried.
|
||||
def initialize(opts={})
|
||||
@resolvers = []
|
||||
@resolvers << opts.fetch(:hosts_resolver, HostsResolver.new)
|
||||
|
||||
if RUBY_VERSION >= '2.1'
|
||||
require 'middleman-core/dns_resolver/local_link_resolver'
|
||||
@resolvers << opts.fetch(:local_link_resolver, LocalLinkResolver.new)
|
||||
end
|
||||
|
||||
@resolvers << opts.fetch(:network_resolver, NetworkResolver.new)
|
||||
end
|
||||
|
||||
# Get names for given ip
|
||||
#
|
||||
# @param [String] ip
|
||||
# The ip which should be resolved.
|
||||
def names_for(ip)
|
||||
resolvers.each do |r|
|
||||
names = r.getnames(ip)
|
||||
|
||||
return names unless names.nil? || names.empty?
|
||||
end
|
||||
|
||||
[]
|
||||
end
|
||||
|
||||
# Get ips for given name
|
||||
#
|
||||
# First the local resolver is used. On POSIX-systems /etc/hosts is used. On
|
||||
# Windows C:\Windows\System32\drivers\etc\hosts is used.
|
||||
#
|
||||
# @param [String] name
|
||||
# The name which should be resolved.
|
||||
def ips_for(name)
|
||||
resolvers.each do |r|
|
||||
ips = r.getaddresses(name)
|
||||
|
||||
return ips unless ips.nil? || ips.empty?
|
||||
end
|
||||
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,52 @@
|
|||
module Middleman
|
||||
class DnsResolver
|
||||
# Use network name server to resolve ips and names
|
||||
class BasicNetworkResolver
|
||||
private
|
||||
|
||||
attr_reader :resolver, :timeouts
|
||||
|
||||
public
|
||||
|
||||
def initialize(opts={})
|
||||
@timeouts = opts.fetch(:timeouts, 2)
|
||||
end
|
||||
|
||||
# Get names for ip
|
||||
#
|
||||
# @param [#to_s] ip
|
||||
# The ip to resolve into names
|
||||
#
|
||||
# @return [Array]
|
||||
# Array of Names
|
||||
def getnames(ip)
|
||||
resolver.getnames(ip.to_s).map(&:to_s)
|
||||
rescue Resolv::ResolvError, Errno::EADDRNOTAVAIL
|
||||
[]
|
||||
end
|
||||
|
||||
# Get ips for name
|
||||
#
|
||||
# @param [#to_s] name
|
||||
# The name to resolve into ips
|
||||
#
|
||||
# @return [Array]
|
||||
# Array of ipaddresses
|
||||
def getaddresses(name)
|
||||
resolver.getaddresses(name.to_s).map(&:to_s)
|
||||
rescue Resolv::ResolvError, Errno::EADDRNOTAVAIL
|
||||
[]
|
||||
end
|
||||
|
||||
# Set timeout for lookup
|
||||
#
|
||||
# @param [Integer] value
|
||||
# The timeout value
|
||||
def timeouts=(timeouts)
|
||||
return if RUBY_VERSION < '2'
|
||||
|
||||
resolver.timeouts = timeouts
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,63 @@
|
|||
module Middleman
|
||||
class DnsResolver
|
||||
# Use network name server to resolve ips and names
|
||||
class HostsResolver
|
||||
private
|
||||
|
||||
attr_reader :resolver
|
||||
|
||||
public
|
||||
|
||||
def initialize(opts={})
|
||||
# using the splat operator works around a non-existing HOSTSRC variable
|
||||
# using nil as input does not work, but `*[]` does and then Resolv::Hosts
|
||||
# uses its defaults
|
||||
@resolver = opts.fetch(:resolver, Resolv::Hosts.new(*hosts_file))
|
||||
end
|
||||
|
||||
# Get names for ip
|
||||
#
|
||||
# @param [#to_s] ip
|
||||
# The ip to resolve into names
|
||||
#
|
||||
# @return [Array]
|
||||
# Array of Names
|
||||
def getnames(ip)
|
||||
resolver.getnames(ip.to_s).map(&:to_s)
|
||||
rescue Resolv::ResolvError
|
||||
[]
|
||||
end
|
||||
|
||||
# Get ips for name
|
||||
#
|
||||
# @param [#to_s] name
|
||||
# The name to resolve into ips
|
||||
#
|
||||
# @return [Array]
|
||||
# Array of ipaddresses
|
||||
def getaddresses(name)
|
||||
resolver.getaddresses(name.to_s).map(&:to_s)
|
||||
rescue Resolv::ResolvError
|
||||
[]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Path to hosts file
|
||||
#
|
||||
# This looks for MM_HOSTSRC in your environment
|
||||
#
|
||||
# @return [Array]
|
||||
# This needs to be an array, to make the splat operator work
|
||||
#
|
||||
# @example
|
||||
# # <ip> <hostname>
|
||||
# 127.0.0.1 localhost.localhost localhost
|
||||
def hosts_file
|
||||
return [ENV['MM_HOSTSRC']] if ENV.key?('MM_HOSTSRC') && File.file?(ENV['MM_HOSTSRC'])
|
||||
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
require 'middleman-core/dns_resolver/basic_network_resolver'
|
||||
|
||||
module Middleman
|
||||
class DnsResolver
|
||||
# Use network name server to resolve ips and names
|
||||
class LocalLinkResolver < BasicNetworkResolver
|
||||
def initialize(opts={})
|
||||
super
|
||||
|
||||
@timeouts = opts.fetch(:timeouts, 1)
|
||||
@resolver = opts.fetch(:resolver, Resolv::MDNS.new(nameserver_config))
|
||||
|
||||
self.timeouts = timeouts
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Hosts + Ports for MDNS resolver
|
||||
#
|
||||
# This looks for MM_MDNSRC in your environment. If you are going to use
|
||||
# IPv6-addresses: Make sure you do not forget to add the port at the end.
|
||||
#
|
||||
# MM_MDNSRC=ip:port ip:port
|
||||
#
|
||||
# @return [Hash]
|
||||
# Returns the configuration for the nameserver
|
||||
#
|
||||
# @example
|
||||
# export MM_MDNSRC="224.0.0.251:5353 ff02::fb:5353"
|
||||
#
|
||||
def nameserver_config
|
||||
return unless ENV.key?('MM_MDNSRC') && ENV['MM_MDNSRC']
|
||||
|
||||
address, port = ENV['MM_MDNSRC'].split(/:/)
|
||||
|
||||
{
|
||||
nameserver_port: [[address, port.to_i]]
|
||||
}
|
||||
rescue StandardError
|
||||
{}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,42 @@
|
|||
require 'middleman-core/dns_resolver/basic_network_resolver'
|
||||
|
||||
module Middleman
|
||||
class DnsResolver
|
||||
# Use network name server to resolve ips and names
|
||||
class NetworkResolver < BasicNetworkResolver
|
||||
def initialize(opts={})
|
||||
super
|
||||
|
||||
@resolver = opts.fetch(:resolver, Resolv::DNS.new(nameserver_config))
|
||||
self.timeouts = timeouts
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Hosts + Ports for MDNS resolver
|
||||
#
|
||||
# This looks for MM_MDNSRC in your environment. If you are going to use
|
||||
# IPv6-addresses: Make sure you do not forget to add the port at the end.
|
||||
#
|
||||
# MM_MDNSRC=ip:port ip:port
|
||||
#
|
||||
# @return [Hash]
|
||||
# Returns the configuration for the nameserver
|
||||
#
|
||||
# @example
|
||||
# export MM_MDNSRC="224.0.0.251:5353 ff02::fb:5353"
|
||||
#
|
||||
def nameserver_config
|
||||
return unless ENV.key?('MM_DNSRC') && ENV['MM_DNSRC']
|
||||
|
||||
address, port = ENV['MM_DNSRC'].split(/:/)
|
||||
|
||||
{
|
||||
nameserver_port: [[address, port.to_i]]
|
||||
}
|
||||
rescue StandardError
|
||||
{}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,7 +3,7 @@ require 'middleman-core/util'
|
|||
require 'middleman-core/rack'
|
||||
|
||||
class Middleman::Extensions::AssetHash < ::Middleman::Extension
|
||||
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.'
|
||||
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.'
|
||||
option :ignore, [], 'Regexes of filenames to skip adding asset hashes to'
|
||||
|
||||
def initialize(app, options_hash={}, &block)
|
||||
|
@ -20,7 +20,7 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
|
|||
|
||||
app.use ::Middleman::Middleware::InlineURLRewriter,
|
||||
id: :asset_hash,
|
||||
url_extensions: options.exts,
|
||||
url_extensions: options.exts.sort.reverse,
|
||||
source_extensions: %w(.htm .html .php .css .js),
|
||||
ignore: @ignore,
|
||||
middleman_app: app,
|
||||
|
|
|
@ -24,6 +24,13 @@ class Middleman::Extensions::AutomaticImageSizes < ::Middleman::Extension
|
|||
if file && file[:full_path].exist?
|
||||
begin
|
||||
width, height = ::FastImage.size(file[:full_path].to_s, raise_on_failure: true)
|
||||
# Check for @2x and @3x image
|
||||
retina = full_path.match(/@(\d)x\.[a-zA-Z]{3,4}$/)
|
||||
if retina
|
||||
factor = retina[1].to_i
|
||||
width /= factor
|
||||
height /= factor
|
||||
end
|
||||
params[:width] = width
|
||||
params[:height] = height
|
||||
rescue FastImage::UnknownImageType
|
||||
|
|
|
@ -8,12 +8,16 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
|
|||
require 'sass'
|
||||
SassCompressor
|
||||
}, 'Set the CSS compressor to use.'
|
||||
option :content_types, %w(text/css), 'Content types of resources that contain CSS'
|
||||
option :inline_content_types, %w(text/html text/php), 'Content types of resources that contain inline CSS'
|
||||
|
||||
def ready
|
||||
# Setup Rack middleware to minify CSS
|
||||
app.use Rack, compressor: options[:compressor],
|
||||
ignore: Array(options[:ignore]) + [/\.min\./],
|
||||
inline: options[:inline]
|
||||
inline: options[:inline],
|
||||
content_types: options[:content_types],
|
||||
inline_content_types: options[:inline_content_types]
|
||||
end
|
||||
|
||||
class SassCompressor
|
||||
|
@ -45,6 +49,8 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
|
|||
@compressor = options.fetch(:compressor)
|
||||
@compressor = @compressor.to_proc if @compressor.respond_to? :to_proc
|
||||
@compressor = @compressor.call if @compressor.is_a? Proc
|
||||
@content_types = options[:content_types]
|
||||
@inline_content_types = options[:inline_content_types]
|
||||
end
|
||||
|
||||
# Rack interface
|
||||
|
@ -53,19 +59,18 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
|
|||
def call(env)
|
||||
status, headers, response = @app.call(env)
|
||||
|
||||
if inline_html_content?(env['PATH_INFO'])
|
||||
minified = ::Middleman::Util.extract_response_text(response)
|
||||
minified.gsub!(INLINE_CSS_REGEX) do
|
||||
$1 << @compressor.compress($2) << $3
|
||||
end
|
||||
content_type = headers['Content-Type'].try(:slice, /^[^;]*/)
|
||||
path = env['PATH_INFO']
|
||||
|
||||
minified = if @inline && minifiable_inline?(content_type)
|
||||
minify_inline(::Middleman::Util.extract_response_text(response))
|
||||
elsif minifiable?(content_type) && !ignore?(path)
|
||||
minify(::Middleman::Util.extract_response_text(response))
|
||||
end
|
||||
|
||||
if minified
|
||||
headers['Content-Length'] = ::Rack::Utils.bytesize(minified).to_s
|
||||
response = [minified]
|
||||
elsif standalone_css_content?(env['PATH_INFO'])
|
||||
minified_css = @compressor.compress(::Middleman::Util.extract_response_text(response))
|
||||
|
||||
headers['Content-Length'] = ::Rack::Utils.bytesize(minified_css).to_s
|
||||
response = [minified_css]
|
||||
end
|
||||
|
||||
[status, headers, response]
|
||||
|
@ -73,14 +78,41 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
|
|||
|
||||
private
|
||||
|
||||
Contract String => Bool
|
||||
def inline_html_content?(path)
|
||||
(path.end_with?('.html') || path.end_with?('.php')) && @inline
|
||||
# Whether the path should be ignored
|
||||
# @param [String] path
|
||||
# @return [Boolean]
|
||||
def ignore?(path)
|
||||
@ignore.any? { |ignore| Middleman::Util.path_match(ignore, path) }
|
||||
end
|
||||
|
||||
Contract String => Bool
|
||||
def standalone_css_content?(path)
|
||||
path.end_with?('.css') && @ignore.none? { |ignore| Middleman::Util.path_match(ignore, path) }
|
||||
# Whether this type of content can be minified
|
||||
# @param [String, nil] content_type
|
||||
# @return [Boolean]
|
||||
def minifiable?(content_type)
|
||||
@content_types.include?(content_type)
|
||||
end
|
||||
|
||||
# Whether this type of content contains inline content that can be minified
|
||||
# @param [String, nil] content_type
|
||||
# @return [Boolean]
|
||||
def minifiable_inline?(content_type)
|
||||
@inline_content_types.include?(content_type)
|
||||
end
|
||||
|
||||
# Minify the content
|
||||
# @param [String] content
|
||||
# @return [String]
|
||||
def minify(content)
|
||||
@compressor.compress(content)
|
||||
end
|
||||
|
||||
# Detect and minify inline content
|
||||
# @param [String] content
|
||||
# @return [String]
|
||||
def minify_inline(content)
|
||||
content.gsub(INLINE_CSS_REGEX) do
|
||||
$1 + minify($2) + $3
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,17 +8,22 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
|
|||
require 'uglifier'
|
||||
::Uglifier.new
|
||||
}, 'Set the JS compressor to use.'
|
||||
option :content_types, %w(application/javascript), 'Content types of resources that contain JS'
|
||||
option :inline_content_types, %w(text/html text/php), 'Content types of resources that contain inline JS'
|
||||
|
||||
def ready
|
||||
# Setup Rack middleware to minify CSS
|
||||
app.use Rack, compressor: options[:compressor],
|
||||
# Setup Rack middleware to minify JS
|
||||
app.use Rack, compressor: chosen_compressor,
|
||||
ignore: Array(options[:ignore]) + [/\.min\./],
|
||||
inline: options[:inline]
|
||||
inline: options[:inline],
|
||||
content_types: options[:content_types],
|
||||
inline_content_types: options[:inline_content_types]
|
||||
end
|
||||
|
||||
# Rack middleware to look for JS and compress it
|
||||
class Rack
|
||||
include Contracts
|
||||
INLINE_JS_REGEX = /(<script[^>]*>\s*(?:\/\/(?:(?:<!--)|(?:<!\[CDATA\[))\n)?)(.*?)((?:(?:\n\s*)?\/\/(?:(?:-->)|(?:\]\]>)))?\s*<\/script>)/m
|
||||
|
||||
# Init
|
||||
# @param [Class] app
|
||||
|
@ -36,6 +41,8 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
|
|||
@compressor = options.fetch(:compressor)
|
||||
@compressor = @compressor.to_proc if @compressor.respond_to? :to_proc
|
||||
@compressor = @compressor.call if @compressor.is_a? Proc
|
||||
@content_types = options[:content_types]
|
||||
@inline_content_types = options[:inline_content_types]
|
||||
end
|
||||
|
||||
# Rack interface
|
||||
|
@ -44,25 +51,18 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
|
|||
def call(env)
|
||||
status, headers, response = @app.call(env)
|
||||
|
||||
path = env['PATH_INFO']
|
||||
type = headers['Content-Type'].try(:slice, /^[^;]*/)
|
||||
@path = env['PATH_INFO']
|
||||
|
||||
begin
|
||||
if @inline && (path.end_with?('.html') || path.end_with?('.php'))
|
||||
uncompressed_source = ::Middleman::Util.extract_response_text(response)
|
||||
minified = if @inline && minifiable_inline?(type)
|
||||
minify_inline(::Middleman::Util.extract_response_text(response))
|
||||
elsif minifiable?(type) && !ignore?(@path)
|
||||
minify(::Middleman::Util.extract_response_text(response))
|
||||
end
|
||||
|
||||
minified = minify_inline_content(uncompressed_source)
|
||||
|
||||
headers['Content-Length'] = ::Rack::Utils.bytesize(minified).to_s
|
||||
response = [minified]
|
||||
elsif path.end_with?('.js') && @ignore.none? { |ignore| Middleman::Util.path_match(ignore, path) }
|
||||
uncompressed_source = ::Middleman::Util.extract_response_text(response)
|
||||
minified = @compressor.compress(uncompressed_source)
|
||||
|
||||
headers['Content-Length'] = ::Rack::Utils.bytesize(minified).to_s
|
||||
response = [minified]
|
||||
end
|
||||
rescue ExecJS::ProgramError => e
|
||||
warn "WARNING: Couldn't compress JavaScript in #{path}: #{e.message}"
|
||||
if minified
|
||||
headers['Content-Length'] = ::Rack::Utils.bytesize(minified).to_s
|
||||
response = [minified]
|
||||
end
|
||||
|
||||
[status, headers, response]
|
||||
|
@ -70,20 +70,50 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
|
|||
|
||||
private
|
||||
|
||||
Contract String => String
|
||||
def minify_inline_content(uncompressed_source)
|
||||
uncompressed_source.gsub(/(<script[^>]*>\s*(?:\/\/(?:(?:<!--)|(?:<!\[CDATA\[))\n)?)(.*?)((?:(?:\n\s*)?\/\/(?:(?:-->)|(?:\]\]>)))?\s*<\/script>)/m) do |match|
|
||||
# Whether the path should be ignored
|
||||
# @param [String] path
|
||||
# @return [Boolean]
|
||||
def ignore?(path)
|
||||
@ignore.any? { |ignore| Middleman::Util.path_match(ignore, path) }
|
||||
end
|
||||
|
||||
# Whether this type of content can be minified
|
||||
# @param [String, nil] content_type
|
||||
# @return [Boolean]
|
||||
def minifiable?(content_type)
|
||||
@content_types.include?(content_type)
|
||||
end
|
||||
|
||||
# Whether this type of content contains inline content that can be minified
|
||||
# @param [String, nil] content_type
|
||||
# @return [Boolean]
|
||||
def minifiable_inline?(content_type)
|
||||
@inline_content_types.include?(content_type)
|
||||
end
|
||||
|
||||
# Minify the content
|
||||
# @param [String] content
|
||||
# @return [String]
|
||||
def minify(content)
|
||||
@compressor.compress(content)
|
||||
rescue ExecJS::ProgramError => e
|
||||
warn "WARNING: Couldn't compress JavaScript in #{@path}: #{e.message}"
|
||||
content
|
||||
end
|
||||
|
||||
# Detect and minify inline content
|
||||
# @param [String] content
|
||||
# @return [String]
|
||||
def minify_inline(content)
|
||||
content.gsub(INLINE_JS_REGEX) do |match|
|
||||
first = $1
|
||||
javascript = $2
|
||||
inline_content = $2
|
||||
last = $3
|
||||
|
||||
# Only compress script tags that contain JavaScript (as opposed
|
||||
# to something like jQuery templates, identified with a "text/html"
|
||||
# type.
|
||||
if first =~ /<script>/ || first.include?('text/javascript')
|
||||
minified_js = @compressor.compress(javascript)
|
||||
|
||||
first << minified_js << last
|
||||
# Only compress script tags that contain JavaScript (as opposed to
|
||||
# something like jQuery templates, identified with a "text/html" type).
|
||||
if first.include?('<script>') || first.include?('text/javascript')
|
||||
first + minify(inline_content) + last
|
||||
else
|
||||
match
|
||||
end
|
||||
|
|
|
@ -22,6 +22,19 @@ class Middleman::Extensions::RelativeAssets < ::Middleman::Extension
|
|||
proc: method(:rewrite_url)
|
||||
end
|
||||
|
||||
helpers do
|
||||
# asset_url override for relative assets
|
||||
# @param [String] path
|
||||
# @param [String] prefix
|
||||
# @param [Hash] options Additional options.
|
||||
# @return [String]
|
||||
def asset_url(path, prefix='', options={})
|
||||
options[:relative] = true unless options.key?(:relative)
|
||||
|
||||
super(path, prefix, options)
|
||||
end
|
||||
end
|
||||
|
||||
Contract String, Or[String, Pathname], Any => Maybe[String]
|
||||
def rewrite_url(asset_path, dirpath, request_path)
|
||||
uri = ::Addressable::URI.parse(asset_path)
|
||||
|
|
|
@ -29,7 +29,7 @@ module Middleman
|
|||
# @param [Class] context
|
||||
# @return [String]
|
||||
Contract Hash, Hash, Any, Maybe[Proc] => String
|
||||
def render(locs={}, opts={}, context, &block)
|
||||
def render(locs, opts, context, &block)
|
||||
path = @path.dup
|
||||
|
||||
# Detect the remdering engine from the extension
|
||||
|
|
|
@ -95,7 +95,7 @@ module Middleman
|
|||
# Render a template with the given name and locals
|
||||
def template(template_name, locals={})
|
||||
template_path = File.join(File.dirname(__FILE__), 'meta_pages', 'templates', template_name)
|
||||
content = Tilt.new(template_path).render(nil, locals)
|
||||
content = Tilt.new(template_path).render(::Object.new, locals)
|
||||
response(content)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,32 +1,53 @@
|
|||
require 'webrick'
|
||||
require 'webrick/https'
|
||||
require 'openssl'
|
||||
require 'socket'
|
||||
require 'middleman-core/meta_pages'
|
||||
require 'middleman-core/logger'
|
||||
require 'middleman-core/rack'
|
||||
require 'middleman-core/preview_server/server_information'
|
||||
require 'middleman-core/preview_server/server_url'
|
||||
|
||||
# rubocop:disable GlobalVars
|
||||
module Middleman
|
||||
module PreviewServer
|
||||
class PreviewServer
|
||||
class << self
|
||||
extend Forwardable
|
||||
|
||||
attr_reader :app, :host, :port, :ssl_certificate, :ssl_private_key
|
||||
attr_reader :app, :ssl_certificate, :ssl_private_key, :environment, :server_information
|
||||
def_delegator :app, :logger
|
||||
|
||||
def https?
|
||||
@https
|
||||
@https == true
|
||||
end
|
||||
|
||||
# Start an instance of Middleman::Application
|
||||
# @return [void]
|
||||
def start(opts={})
|
||||
@options = opts
|
||||
# Do not buffer output, otherwise testing of output does not work
|
||||
$stdout.sync = true
|
||||
$stderr.sync = true
|
||||
|
||||
mount_instance(new_app)
|
||||
logger.info "== The Middleman is standing watch at #{uri}"
|
||||
logger.info "== Inspect your site configuration at #{uri + '__middleman'}"
|
||||
@options = opts
|
||||
@server_information = ServerInformation.new
|
||||
|
||||
# New app evaluates the middleman configuration. Since this can be
|
||||
# invalid as well, we need to evaluate the configuration BEFORE
|
||||
# checking for validity
|
||||
the_app = initialize_new_app
|
||||
|
||||
# And now comes the check
|
||||
unless server_information.valid?
|
||||
$stderr.puts %(== Running Middleman failed: #{server_information.reason}. Please fix that and try again.)
|
||||
exit 1
|
||||
end
|
||||
|
||||
mount_instance(the_app)
|
||||
|
||||
logger.debug %(== Server information is provided by #{server_information.handler})
|
||||
logger.debug %(== The Middleman is running in "#{environment}" environment)
|
||||
logger.debug format('== The Middleman preview server is bound to %s', ServerUrl.new(hosts: server_information.listeners, port: server_information.port, https: https?).to_bind_addresses.join(', '))
|
||||
logger.info format('== View your site at %s', ServerUrl.new(hosts: server_information.site_addresses, port: server_information.port, https: https?).to_urls.join(', '))
|
||||
logger.info format('== Inspect your site configuration at %s', ServerUrl.new(hosts: server_information.site_addresses, port: server_information.port, https: https?).to_config_urls.join(', '))
|
||||
|
||||
@initialized ||= false
|
||||
return if @initialized
|
||||
|
@ -70,7 +91,7 @@ module Middleman
|
|||
logger.info '== The Middleman is reloading'
|
||||
|
||||
begin
|
||||
app = new_app
|
||||
app = initialize_new_app
|
||||
rescue => e
|
||||
logger.error "Error reloading Middleman: #{e}\n#{e.backtrace.join("\n")}"
|
||||
logger.info '== The Middleman is still running the application from before the error'
|
||||
|
@ -96,7 +117,7 @@ module Middleman
|
|||
|
||||
private
|
||||
|
||||
def new_app
|
||||
def initialize_new_app
|
||||
opts = @options.dup
|
||||
|
||||
::Middleman::Logger.singleton(
|
||||
|
@ -110,8 +131,10 @@ module Middleman
|
|||
config[:watcher_force_polling] = opts[:force_polling]
|
||||
config[:watcher_latency] = opts[:latency]
|
||||
|
||||
config[:host] = opts[:host] if opts[:host]
|
||||
config[:port] = opts[:port] if opts[:port]
|
||||
config[:bind_address] = opts[:bind_address]
|
||||
config[:server_name] = opts[:server_name]
|
||||
config[:https] = opts[:https] unless opts[:https].nil?
|
||||
config[:ssl_certificate] = opts[:ssl_certificate] if opts[:ssl_certificate]
|
||||
config[:ssl_private_key] = opts[:ssl_private_key] if opts[:ssl_private_key]
|
||||
|
||||
|
@ -139,9 +162,17 @@ module Middleman
|
|||
end
|
||||
end
|
||||
|
||||
@host = app.config[:host]
|
||||
@port = app.config[:port]
|
||||
@https = app.config[:https]
|
||||
# store configured port to make a check later on possible
|
||||
configured_port = app.config[:port]
|
||||
|
||||
# Use configuration values to set `bind_address` etc. in
|
||||
# `server_information`
|
||||
server_information.use app.config
|
||||
|
||||
logger.warn format('== The Middleman uses a different port "%s" then the configured one "%s" because some other server is listening on that port.', server_information.port, configured_port) unless app.config[:port] == configured_port
|
||||
|
||||
@https = app.config[:https]
|
||||
@environment = app.config[:environment]
|
||||
|
||||
@ssl_certificate = app.config[:ssl_certificate]
|
||||
@ssl_private_key = app.config[:ssl_private_key]
|
||||
|
@ -179,9 +210,10 @@ module Middleman
|
|||
# @return [void]
|
||||
def setup_webrick(is_logging)
|
||||
http_opts = {
|
||||
BindAddress: host,
|
||||
Port: port,
|
||||
Port: server_information.port,
|
||||
AccessLog: [],
|
||||
ServerName: server_information.server_name,
|
||||
BindAddress: server_information.bind_address.to_s,
|
||||
DoNotReverseLookup: true
|
||||
}
|
||||
|
||||
|
@ -198,6 +230,9 @@ module Middleman
|
|||
%w(CN localhost),
|
||||
%w(CN #{host})
|
||||
].uniq
|
||||
cert, key = create_self_signed_cert(1024, [['CN', server_information.server_name]], server_information.site_addresses, 'Middleman Preview Server')
|
||||
http_opts[:SSLCertificate] = cert
|
||||
http_opts[:SSLPrivateKey] = key
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -210,11 +245,44 @@ module Middleman
|
|||
begin
|
||||
::WEBrick::HTTPServer.new(http_opts)
|
||||
rescue Errno::EADDRINUSE
|
||||
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}"
|
||||
exit(1)
|
||||
logger.error %(== Port "#{http_opts[:Port]}" is in use. This should not have happened. Please start "middleman server" again.)
|
||||
end
|
||||
end
|
||||
|
||||
# Copy of https://github.com/nahi/ruby/blob/webrick_trunk/lib/webrick/ssl.rb#L39
|
||||
# that uses a different serial number each time the cert is generated in order to
|
||||
# avoid errors in Firefox. Also doesn't print out stuff to $stderr unnecessarily.
|
||||
def create_self_signed_cert(bits, cn, aliases, comment)
|
||||
rsa = OpenSSL::PKey::RSA.new(bits)
|
||||
cert = OpenSSL::X509::Certificate.new
|
||||
cert.version = 2
|
||||
cert.serial = Time.now.to_i % (1 << 20)
|
||||
name = OpenSSL::X509::Name.new(cn)
|
||||
cert.subject = name
|
||||
cert.issuer = name
|
||||
cert.not_before = Time.now
|
||||
cert.not_after = Time.now + (365 * 24 * 60 * 60)
|
||||
cert.public_key = rsa.public_key
|
||||
|
||||
ef = OpenSSL::X509::ExtensionFactory.new(nil, cert)
|
||||
ef.issuer_certificate = cert
|
||||
cert.extensions = [
|
||||
ef.create_extension('basicConstraints', 'CA:FALSE'),
|
||||
ef.create_extension('keyUsage', 'keyEncipherment'),
|
||||
ef.create_extension('subjectKeyIdentifier', 'hash'),
|
||||
ef.create_extension('extendedKeyUsage', 'serverAuth'),
|
||||
ef.create_extension('nsComment', comment)
|
||||
]
|
||||
aki = ef.create_extension('authorityKeyIdentifier',
|
||||
'keyid:always,issuer:always')
|
||||
cert.add_extension(aki)
|
||||
cert.add_extension ef.create_extension('subjectAltName', aliases.map { |d| "DNS: #{d}" }.join(','))
|
||||
|
||||
cert.sign(rsa, OpenSSL::Digest::SHA1.new)
|
||||
|
||||
[cert, rsa]
|
||||
end
|
||||
|
||||
# Attach a new Middleman::Application instance
|
||||
# @param [Middleman::Application] app
|
||||
# @return [void]
|
||||
|
@ -236,23 +304,6 @@ module Middleman
|
|||
|
||||
@app = nil
|
||||
end
|
||||
|
||||
# Returns the URI the preview server will run on
|
||||
# @return [URI]
|
||||
def uri
|
||||
host = (@host == '0.0.0.0') ? 'localhost' : @host
|
||||
scheme = https? ? 'https' : 'http'
|
||||
URI("#{scheme}://#{host}:#{@port}")
|
||||
end
|
||||
|
||||
# Returns unused TCP port
|
||||
# @return [Fixnum]
|
||||
def unused_tcp_port
|
||||
server = TCPServer.open(0)
|
||||
port = server.addr[1]
|
||||
server.close
|
||||
port
|
||||
end
|
||||
end
|
||||
|
||||
class FilteredWebrickLog < ::WEBrick::Log
|
||||
|
|
81
middleman-core/lib/middleman-core/preview_server/checks.rb
Normal file
81
middleman-core/lib/middleman-core/preview_server/checks.rb
Normal file
|
@ -0,0 +1,81 @@
|
|||
require 'ipaddr'
|
||||
|
||||
module Middleman
|
||||
class PreviewServer
|
||||
# Checks for input of preview server
|
||||
module Checks
|
||||
# This one will get all default setup
|
||||
class BasicCheck; end
|
||||
|
||||
# This checks if the server name resolves to the bind_address
|
||||
#
|
||||
# If the users enters:
|
||||
#
|
||||
# 1. server_name: www.example.com (10.0.0.1)
|
||||
# 2. bind_address: 127.0.0.01
|
||||
#
|
||||
# This validation will fail
|
||||
class ServerNameResolvesToBindAddress < BasicCheck
|
||||
private
|
||||
|
||||
attr_reader :resolver
|
||||
|
||||
public
|
||||
|
||||
def initialize
|
||||
@resolver = DnsResolver.new
|
||||
end
|
||||
|
||||
# Validate
|
||||
#
|
||||
# @param [Information] information
|
||||
# The information to be validated
|
||||
def validate(information)
|
||||
return if resolver.ips_for(information.server_name).include? information.bind_address
|
||||
|
||||
information.valid = false
|
||||
information.reason = format('Server name "%s" does not resolve to bind address "%s"', information.server_name, information.bind_address)
|
||||
end
|
||||
end
|
||||
|
||||
# This validation fails if the user chooses to use an ip address which is
|
||||
# not available on his/her system
|
||||
class InterfaceIsAvailableOnSystem < BasicCheck
|
||||
# Validate
|
||||
#
|
||||
# @param [Information] information
|
||||
# The information to be validated
|
||||
def validate(information)
|
||||
return if information.bind_address.blank? || information.local_network_interfaces.include?(information.bind_address.to_s) || %w(0.0.0.0 ::).any? { |b| information.bind_address == b } || IPAddr.new('127.0.0.0/8').include?(information.bind_address.to_s)
|
||||
|
||||
information.valid = false
|
||||
information.reason = format('Bind address "%s" is not available on your system. Please use one of %s', information.bind_address, information.local_network_interfaces.map { |i| %("#{i}") }.join(', '))
|
||||
end
|
||||
end
|
||||
|
||||
# This one requires a bind address if the user entered a server name
|
||||
#
|
||||
# If the `bind_address` is blank this check will fail
|
||||
class RequiresBindAddressIfServerNameIsGiven < BasicCheck
|
||||
def validate(information)
|
||||
return unless information.bind_address.blank?
|
||||
|
||||
information.valid = false
|
||||
information.reason = format('Server name "%s" does not resolve to an ip address', information.server_name)
|
||||
end
|
||||
end
|
||||
|
||||
# This validation always fails
|
||||
class DenyAnyAny < BasicCheck
|
||||
# Validate
|
||||
#
|
||||
# @param [Information] information
|
||||
# The information to be validated
|
||||
def validate(information)
|
||||
information.valid = false
|
||||
information.reason = 'Undefined combination of options "--server-name" and "--bind-address". If you think this is wrong, please file a bug at "https://github.com/middleman/middleman"'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
273
middleman-core/lib/middleman-core/preview_server/information.rb
Normal file
273
middleman-core/lib/middleman-core/preview_server/information.rb
Normal file
|
@ -0,0 +1,273 @@
|
|||
require 'ipaddr'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'middleman-core/preview_server/checks'
|
||||
require 'middleman-core/preview_server/server_hostname'
|
||||
require 'middleman-core/preview_server/server_ip_address'
|
||||
|
||||
module Middleman
|
||||
class PreviewServer
|
||||
# Basic information class to wrap common behaviour
|
||||
class BasicInformation
|
||||
private
|
||||
|
||||
attr_reader :checks, :network_interfaces_inventory
|
||||
|
||||
public
|
||||
|
||||
attr_accessor :bind_address, :server_name, :port, :reason, :valid
|
||||
attr_reader :listeners, :site_addresses
|
||||
|
||||
# Create instance
|
||||
#
|
||||
# @param [String] bind_address
|
||||
# The bind address of the server
|
||||
#
|
||||
# @param [String] server_name
|
||||
# The name of the server
|
||||
#
|
||||
# @param [Integer] port
|
||||
# The port to listen on
|
||||
def initialize(opts={})
|
||||
@bind_address = ServerIpAddress.new(opts[:bind_address])
|
||||
@server_name = ServerHostname.new(opts[:server_name])
|
||||
@port = opts[:port]
|
||||
@valid = true
|
||||
|
||||
@site_addresses = []
|
||||
@listeners = []
|
||||
@checks = []
|
||||
|
||||
# This needs to be check for each use case. Otherwise `Webrick` will
|
||||
# complain about that.
|
||||
@checks << Checks::InterfaceIsAvailableOnSystem.new
|
||||
end
|
||||
|
||||
# Is the given information valid?
|
||||
def valid?
|
||||
valid == true
|
||||
end
|
||||
|
||||
# Pass "self" to validator
|
||||
#
|
||||
# @param [#validate] validator
|
||||
# The validator
|
||||
def validate_me(validator)
|
||||
validator.validate self, checks
|
||||
end
|
||||
|
||||
def resolve_me(*)
|
||||
fail NoMethodError
|
||||
end
|
||||
|
||||
# Get network information
|
||||
#
|
||||
# @param [#network_interfaces] inventory
|
||||
# Get list of available network interfaces
|
||||
def show_me_network_interfaces(inventory)
|
||||
@network_interfaces_inventory = inventory
|
||||
end
|
||||
|
||||
# Default is to get all network interfaces
|
||||
def local_network_interfaces
|
||||
network_interfaces_inventory.nil? ? [] : network_interfaces_inventory.network_interfaces(:all)
|
||||
end
|
||||
end
|
||||
|
||||
# This only is used if no other parser is available
|
||||
#
|
||||
# The "default" behaviour is to fail because of "Checks::DenyAnyAny"
|
||||
class DefaultInformation < BasicInformation
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
# Make this fail
|
||||
@checks << Checks::DenyAnyAny.new
|
||||
end
|
||||
|
||||
def resolve_me(*); end
|
||||
|
||||
# Always true
|
||||
def self.matches?(*)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
# This one is used if no bind address and no server name is given
|
||||
class AllInterfaces < BasicInformation
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
after_init
|
||||
end
|
||||
|
||||
def self.matches?(opts={})
|
||||
opts[:bind_address].blank? && opts[:server_name].blank?
|
||||
end
|
||||
|
||||
# Resolve ips
|
||||
def resolve_me(resolver)
|
||||
hostname = ServerHostname.new(Socket.gethostname)
|
||||
hostname_ips = resolver.ips_for(hostname)
|
||||
network_interface = ServerIpAddress.new(Array(local_network_interfaces).first)
|
||||
resolved_name = ServerHostname.new(resolver.names_for(network_interface).first)
|
||||
|
||||
if includes_array? local_network_interfaces, hostname_ips
|
||||
@server_name = hostname
|
||||
@site_addresses << hostname
|
||||
|
||||
network_interface = ServerIpAddress.new((local_network_interfaces & hostname_ips).first)
|
||||
elsif RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
|
||||
@server_name = hostname
|
||||
@site_addresses << hostname
|
||||
elsif !resolved_name.blank?
|
||||
@server_name = resolved_name
|
||||
@site_addresses << resolved_name
|
||||
else
|
||||
@server_name = network_interface
|
||||
end
|
||||
|
||||
@site_addresses << network_interface
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def includes_array?(a, b)
|
||||
!(a & b).empty?
|
||||
end
|
||||
|
||||
def after_init
|
||||
@listeners << ServerIpAddress.new('::')
|
||||
@listeners << ServerIpAddress.new('0.0.0.0')
|
||||
end
|
||||
end
|
||||
|
||||
# This is used if bind address is 0.0.0.0, the server name needs to be
|
||||
# blank
|
||||
class AllIpv4Interfaces < AllInterfaces
|
||||
def self.matches?(opts={})
|
||||
opts[:bind_address] == '0.0.0.0' && opts[:server_name].blank?
|
||||
end
|
||||
|
||||
# Use only ipv4 interfaces
|
||||
def local_network_interfaces
|
||||
network_interfaces_inventory.nil? ? [] : network_interfaces_inventory.network_interfaces(:ipv4)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def after_init
|
||||
@listeners << ServerIpAddress.new('0.0.0.0')
|
||||
end
|
||||
end
|
||||
|
||||
# This is used if bind address is ::, the server name needs to be blank
|
||||
class AllIpv6Interfaces < AllInterfaces
|
||||
def self.matches?(opts={})
|
||||
opts[:bind_address] == '::' && opts[:server_name].blank?
|
||||
end
|
||||
|
||||
# Use only ipv6 interfaces
|
||||
def local_network_interfaces
|
||||
network_interfaces_inventory.nil? ? [] : network_interfaces_inventory.network_interfaces(:ipv6)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def after_init
|
||||
@listeners << ServerIpAddress.new('::')
|
||||
end
|
||||
end
|
||||
|
||||
# Used if a bind address is given and the server name is blank
|
||||
class BindAddressInformation < BasicInformation
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
@listeners << bind_address
|
||||
@site_addresses << bind_address
|
||||
end
|
||||
|
||||
def self.matches?(opts={})
|
||||
!opts[:bind_address].blank? && opts[:server_name].blank?
|
||||
end
|
||||
|
||||
# Resolv
|
||||
def resolve_me(resolver)
|
||||
@server_name = ServerHostname.new(resolver.names_for(bind_address).first)
|
||||
@site_addresses << @server_name unless @server_name.blank?
|
||||
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
# Use if server name is given and bind address is blank
|
||||
class ServerNameInformation < BasicInformation
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
@checks << Checks::RequiresBindAddressIfServerNameIsGiven.new
|
||||
@site_addresses << server_name
|
||||
end
|
||||
|
||||
def resolve_me(resolver)
|
||||
@bind_address = ServerIpAddress.new(resolver.ips_for(server_name).first)
|
||||
|
||||
unless bind_address.blank?
|
||||
@listeners << bind_address
|
||||
@site_addresses << bind_address
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def self.matches?(opts={})
|
||||
opts[:bind_address].blank? && !opts[:server_name].blank?
|
||||
end
|
||||
end
|
||||
|
||||
# Only used if bind address and server name are given and bind address is
|
||||
# not :: or 0.0.0.0
|
||||
class BindAddressAndServerNameInformation < BasicInformation
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
@listeners << bind_address
|
||||
@site_addresses << server_name
|
||||
@site_addresses << bind_address
|
||||
|
||||
@checks << Checks::ServerNameResolvesToBindAddress.new
|
||||
end
|
||||
|
||||
def self.matches?(opts={})
|
||||
!opts[:bind_address].blank? && !opts[:server_name].blank? && !%w(:: 0.0.0.0).include?(opts[:bind_address])
|
||||
end
|
||||
|
||||
def resolve_me(*); end
|
||||
end
|
||||
|
||||
# If the server name is either an ipv4 or ipv6 address, e.g. 127.0.0.1 or
|
||||
# ::1, use this one
|
||||
class ServerNameIsIpInformation < BasicInformation
|
||||
def initialize(opts={})
|
||||
super
|
||||
|
||||
ip = ServerIpAddress.new(server_name.to_s)
|
||||
|
||||
@listeners << ip
|
||||
@site_addresses << ip
|
||||
end
|
||||
|
||||
def resolve_me(*); end
|
||||
|
||||
def self.matches?(opts={})
|
||||
ip = IPAddr.new(opts[:server_name])
|
||||
|
||||
ip.ipv4? || ip.ipv6?
|
||||
rescue
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,65 @@
|
|||
require 'middleman-core/preview_server/server_ip_address'
|
||||
|
||||
module Middleman
|
||||
class PreviewServer
|
||||
# This holds information about local network interfaces on the user systemd
|
||||
class NetworkInterfaceInventory
|
||||
# Return all ip interfaces
|
||||
class All
|
||||
def network_interfaces
|
||||
ipv4_addresses = Socket.ip_address_list.select(&:ipv4?).map { |ai| ServerIpv4Address.new(ai.ip_address) }
|
||||
ipv6_addresses = Socket.ip_address_list.select(&:ipv6?).map { |ai| ServerIpv6Address.new(ai.ip_address) }
|
||||
|
||||
ipv4_addresses + ipv6_addresses
|
||||
end
|
||||
|
||||
def self.match?(*)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
# Return all ipv4 interfaces
|
||||
class Ipv4
|
||||
def network_interfaces
|
||||
Socket.ip_address_list.select { |ai| ai.ipv4? && !ai.ipv4_loopback? }.map { |ai| ServerIpv4Address.new(ai.ip_address) }
|
||||
end
|
||||
|
||||
def self.match?(type)
|
||||
:ipv4 == type
|
||||
end
|
||||
end
|
||||
|
||||
# Return all ipv6 interfaces
|
||||
class Ipv6
|
||||
def network_interfaces
|
||||
Socket.ip_address_list.select { |ai| ai.ipv6? && !ai.ipv6_loopback? }.map { |ai| ServerIpv6Address.new(ai.ip_address) }
|
||||
end
|
||||
|
||||
def self.match?(type)
|
||||
:ipv6 == type
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :types
|
||||
|
||||
public
|
||||
|
||||
def initialize
|
||||
@types = []
|
||||
@types << Ipv4
|
||||
@types << Ipv6
|
||||
@types << All
|
||||
end
|
||||
|
||||
# Return ip interfaces
|
||||
#
|
||||
# @param [Symbol] type
|
||||
# The type of interface which should be returned
|
||||
def network_interfaces(type=:all)
|
||||
types.find { |t| t.match? type.to_sym }.new.network_interfaces
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
module Middleman
|
||||
class PreviewServer
|
||||
class ServerHostname
|
||||
class ServerFullHostname < SimpleDelegator
|
||||
def to_s
|
||||
__getobj__.gsub(/\s/, '+')
|
||||
end
|
||||
|
||||
def self.match?(*)
|
||||
true
|
||||
end
|
||||
|
||||
alias_method :to_browser, :to_s
|
||||
end
|
||||
|
||||
class ServerPlainHostname < SimpleDelegator
|
||||
def to_s
|
||||
__getobj__.gsub(/\s/, '+') + '.local'
|
||||
end
|
||||
|
||||
def self.match?(name)
|
||||
# rubocop:disable Style/CaseEquality
|
||||
name != 'localhost' && /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?\.?$/ === name
|
||||
# rubocop:enable Style/CaseEquality
|
||||
end
|
||||
|
||||
alias_method :to_browser, :to_s
|
||||
end
|
||||
|
||||
def self.new(string)
|
||||
@names = []
|
||||
@names << ServerPlainHostname
|
||||
@names << ServerFullHostname
|
||||
|
||||
@names.find { |n| n.match? string }.new(string)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,144 @@
|
|||
require 'middleman-core/dns_resolver'
|
||||
require 'middleman-core/preview_server/information'
|
||||
require 'middleman-core/preview_server/network_interface_inventory'
|
||||
require 'middleman-core/preview_server/tcp_port_prober'
|
||||
require 'middleman-core/preview_server/server_information_validator'
|
||||
|
||||
module Middleman
|
||||
class PreviewServer
|
||||
# This class holds all information which the preview server needs to setup a listener
|
||||
#
|
||||
# * server name
|
||||
# * bind address
|
||||
# * port
|
||||
#
|
||||
# Furthermore it probes for a free tcp port, if the default one 4567 is not available.
|
||||
class ServerInformation
|
||||
private
|
||||
|
||||
attr_reader :resolver, :validator, :network_interface_inventory, :informations, :tcp_port_prober
|
||||
|
||||
public
|
||||
|
||||
def initialize(opts={})
|
||||
@resolver = opts.fetch(:resolver, DnsResolver.new)
|
||||
@validator = opts.fetch(:validator, ServerInformationValidator.new)
|
||||
@network_interface_inventory = opts.fetch(:network_interface_inventory, NetworkInterfaceInventory.new)
|
||||
@tcp_port_prober = opts.fetch(:tcp_port_prober, TcpPortProber.new)
|
||||
|
||||
@informations = []
|
||||
@informations << AllInterfaces
|
||||
@informations << AllIpv4Interfaces
|
||||
@informations << AllIpv6Interfaces
|
||||
@informations << ServerNameIsIpInformation
|
||||
@informations << ServerNameInformation
|
||||
@informations << BindAddressInformation
|
||||
@informations << BindAddressAndServerNameInformation
|
||||
@informations << DefaultInformation
|
||||
end
|
||||
|
||||
# The information
|
||||
#
|
||||
# Is cached
|
||||
def information
|
||||
return @information if @information
|
||||
|
||||
# The `DefaultInformation`-class always returns `true`, so there's
|
||||
# always a klass available and find will never return nil
|
||||
listener_klass = informations.find { |l| l.matches? bind_address: @bind_address, server_name: @server_name }
|
||||
@information = listener_klass.new(bind_address: @bind_address, server_name: @server_name)
|
||||
|
||||
@information.show_me_network_interfaces(network_interface_inventory)
|
||||
@information.resolve_me(resolver)
|
||||
@information.port = tcp_port_prober.port(@port)
|
||||
@information.validate_me(validator)
|
||||
|
||||
@information
|
||||
end
|
||||
|
||||
# Use a middleman configuration to get information
|
||||
#
|
||||
# @param [#[]] config
|
||||
# The middleman config
|
||||
def use(config)
|
||||
@bind_address = config[:bind_address]
|
||||
@port = config[:port]
|
||||
@server_name = config[:server_name]
|
||||
|
||||
config[:bind_address] = bind_address
|
||||
config[:port] = port
|
||||
config[:server_name] = server_name
|
||||
end
|
||||
|
||||
# Make information of internal server class avaible to make debugging
|
||||
# easier. This can be used to log the class which was used to determine
|
||||
# the preview server settings
|
||||
#
|
||||
# @return [String]
|
||||
# The name of the class
|
||||
def handler
|
||||
information.class.to_s
|
||||
end
|
||||
|
||||
# Is the server information valid?
|
||||
#
|
||||
# This is used to output a helpful error message, which can be stored in
|
||||
# `#reason`.
|
||||
#
|
||||
# @return [TrueClass, FalseClass]
|
||||
# The result
|
||||
def valid?
|
||||
information.valid?
|
||||
end
|
||||
|
||||
# The reason why the information is NOT valid
|
||||
#
|
||||
# @return [String]
|
||||
# The reason why the information is not valid
|
||||
def reason
|
||||
information.reason
|
||||
end
|
||||
|
||||
# The server name
|
||||
#
|
||||
# @return [String]
|
||||
# The name of the server
|
||||
def server_name
|
||||
information.server_name
|
||||
end
|
||||
|
||||
# The bind address of server
|
||||
#
|
||||
# @return [String]
|
||||
# The bind address of the server
|
||||
def bind_address
|
||||
information.bind_address
|
||||
end
|
||||
|
||||
# The port on which the server should listen
|
||||
#
|
||||
# @return [Integer]
|
||||
# The port number
|
||||
def port
|
||||
information.port
|
||||
end
|
||||
|
||||
# A list of site addresses
|
||||
#
|
||||
# @return [Array]
|
||||
# A list of addresses which can be used to access the middleman preview
|
||||
# server
|
||||
def site_addresses
|
||||
information.site_addresses
|
||||
end
|
||||
|
||||
# A list of listeners
|
||||
#
|
||||
# @return [Array]
|
||||
# A list of bind address where the
|
||||
def listeners
|
||||
information.listeners
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
module Middleman
|
||||
class PreviewServer
|
||||
# Validate user input
|
||||
class ServerInformationValidator
|
||||
# Validate the input
|
||||
#
|
||||
# @param [ServerInformation] information
|
||||
# The information instance which holds information about the preview
|
||||
# server settings
|
||||
#
|
||||
# @param [Array] checks
|
||||
# A list of checks which should be evaluated
|
||||
def validate(information, checks)
|
||||
checks.each { |c| c.validate information }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,55 @@
|
|||
require 'ipaddr'
|
||||
require 'forwardable'
|
||||
|
||||
module Middleman
|
||||
class PreviewServer
|
||||
class ServerIpAddress
|
||||
def self.new(ip_address)
|
||||
@parser = []
|
||||
@parser << ServerIpv6Address
|
||||
@parser << ServerIpv4Address
|
||||
|
||||
@parser.find { |p| p.match? ip_address }.new(ip_address)
|
||||
end
|
||||
end
|
||||
|
||||
class BasicServerIpAddress < SimpleDelegator
|
||||
end
|
||||
|
||||
class ServerIpv4Address < BasicServerIpAddress
|
||||
def to_browser
|
||||
__getobj__.to_s
|
||||
end
|
||||
|
||||
def self.match?(*)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
class ServerIpv6Address < BasicServerIpAddress
|
||||
def to_s
|
||||
__getobj__.sub(/%.*$/, '')
|
||||
end
|
||||
|
||||
def to_browser
|
||||
format('[%s]', to_s)
|
||||
end
|
||||
|
||||
if RUBY_VERSION < '2'
|
||||
def self.match?(str)
|
||||
str = str.to_s.sub(/%.*$/, '')
|
||||
IPAddr.new(str).ipv6?
|
||||
rescue StandardError
|
||||
false
|
||||
end
|
||||
else
|
||||
def self.match?(str)
|
||||
str = str.to_s.sub(/%.*$/, '')
|
||||
IPAddr.new(str).ipv6?
|
||||
rescue IPAddr::InvalidAddressError, IPAddr::AddressFamilyError
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,50 @@
|
|||
require 'ipaddr'
|
||||
|
||||
module Middleman
|
||||
class PreviewServer
|
||||
# This builds the server urls for the preview server
|
||||
class ServerUrl
|
||||
private
|
||||
|
||||
attr_reader :hosts, :port, :https
|
||||
|
||||
public
|
||||
|
||||
def initialize(opts={})
|
||||
@hosts = opts.fetch(:hosts)
|
||||
@port = opts.fetch(:port)
|
||||
@https = opts.fetch(:https, false)
|
||||
end
|
||||
|
||||
# Return bind addresses
|
||||
#
|
||||
# @return [Array]
|
||||
# List of bind addresses of format host:port
|
||||
def to_bind_addresses
|
||||
hosts.map { |l| format('"%s:%s"', l.to_s, port) }
|
||||
end
|
||||
|
||||
# Return server urls
|
||||
#
|
||||
# @return [Array]
|
||||
# List of urls of format http://host:port
|
||||
def to_urls
|
||||
hosts.map { |l| format('"%s://%s:%s"', https? ? 'https' : 'http', l.to_browser, port) }
|
||||
end
|
||||
|
||||
# Return server config urls
|
||||
#
|
||||
# @return [Array]
|
||||
# List of urls of format http://host:port/__middleman
|
||||
def to_config_urls
|
||||
hosts.map { |l| format('"%s://%s:%s/__middleman"', https? ? 'https' : 'http', l.to_browser, port) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def https?
|
||||
https == true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,29 @@
|
|||
module Middleman
|
||||
class PreviewServer
|
||||
# Probe for tcp ports
|
||||
#
|
||||
# This one first tries `try_port` if this is not available use the free
|
||||
# port returned by TCPServer.
|
||||
class TcpPortProber
|
||||
# Check for port
|
||||
#
|
||||
# @param [Integer] try_port
|
||||
# The port to be checked
|
||||
#
|
||||
# @return [Integer]
|
||||
# The port
|
||||
def port(try_port)
|
||||
server = TCPServer.open(try_port)
|
||||
server.close
|
||||
|
||||
try_port
|
||||
rescue
|
||||
server = TCPServer.open(0)
|
||||
port = server.addr[1]
|
||||
server.close
|
||||
|
||||
port
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -30,7 +30,11 @@ module Middleman
|
|||
elsif path.is_a? String
|
||||
path_clean = ::Middleman::Util.normalize_path(path)
|
||||
if path_clean.include?('*') # It's a glob
|
||||
@ignored_callbacks << proc { |p| File.fnmatch(path_clean, p) }
|
||||
if defined? File::FNM_EXTGLOB
|
||||
@ignored_callbacks << proc { |p| File.fnmatch(path_clean, p, File::FNM_EXTGLOB) }
|
||||
else
|
||||
@ignored_callbacks << proc { |p| File.fnmatch(path_clean, p) }
|
||||
end
|
||||
else
|
||||
# Add a specific-path ignore unless that path is already covered
|
||||
return if ignored?(path_clean)
|
||||
|
|
|
@ -74,6 +74,7 @@ module Middleman
|
|||
<<-END
|
||||
<html>
|
||||
<head>
|
||||
<link rel="canonical" href="#{url}" />
|
||||
<meta http-equiv=refresh content="0; url=#{url}" />
|
||||
<meta name="robots" content="noindex,follow" />
|
||||
<meta http-equiv="cache-control" content="no-cache" />
|
||||
|
|
|
@ -212,10 +212,10 @@ module Middleman
|
|||
private
|
||||
|
||||
def reset_lookup_cache!
|
||||
@lock.synchronize {
|
||||
@lock.synchronize do
|
||||
@_lookup_by_path = {}
|
||||
@_lookup_by_destination_path = {}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# Removes the templating extensions, while keeping the others
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
require 'hamster'
|
||||
require 'middleman-core/contracts'
|
||||
require 'backports/2.0.0/enumerable/lazy'
|
||||
|
||||
module Middleman
|
||||
# The standard "record" that contains information about a file on disk.
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# Watcher Library
|
||||
require 'listen'
|
||||
require 'middleman-core/contracts'
|
||||
require 'middleman-core/contracts'
|
||||
require 'backports/2.0.0/enumerable/lazy'
|
||||
|
||||
# Monkey patch Listen silencer so `only` works on directories too
|
||||
module Listen
|
||||
|
@ -123,6 +121,8 @@ module Middleman
|
|||
# @return [Middleman::SourceFile, nil]
|
||||
Contract Or[String, Pathname], Maybe[Bool] => Maybe[IsA['Middleman::SourceFile']]
|
||||
def find(path, glob=false)
|
||||
path = path.to_s.encode!('UTF-8', 'UTF-8-MAC') if RUBY_PLATFORM =~ /darwin/
|
||||
|
||||
p = Pathname(path)
|
||||
|
||||
return nil if p.absolute? && !p.to_s.start_with?(@directory.to_s)
|
||||
|
|
|
@ -2,6 +2,7 @@ require 'aruba/cucumber'
|
|||
require 'middleman-core/step_definitions/middleman_steps'
|
||||
require 'middleman-core/step_definitions/builder_steps'
|
||||
require 'middleman-core/step_definitions/server_steps'
|
||||
require 'middleman-core/step_definitions/commandline_steps'
|
||||
|
||||
# Monkeypatch for windows support
|
||||
module ArubaMonkeypatch
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
When /^I stop (?:middleman|all commands) if the output( of the last command)? contains:$/ do |last_command, expected|
|
||||
begin
|
||||
Timeout.timeout(exit_timeout) do
|
||||
loop do
|
||||
fail "You need to start middleman interactively first." unless @interactive
|
||||
|
||||
if unescape(@interactive.output) =~ Regexp.new(unescape(expected))
|
||||
only_processes.each { |p| p.terminate }
|
||||
break
|
||||
end
|
||||
|
||||
sleep 0.1
|
||||
end
|
||||
end
|
||||
rescue ChildProcess::TimeoutError, TimeoutError
|
||||
@interactive.terminate
|
||||
ensure
|
||||
announcer.stdout @interactive.stdout
|
||||
announcer.stderr @interactive.stderr
|
||||
end
|
||||
end
|
||||
|
||||
# Make it just a long running process
|
||||
Given /`(.*?)` is running in background/ do |cmd|
|
||||
run(cmd, 120)
|
||||
end
|
||||
|
||||
Given /I have a local hosts file with:/ do |string|
|
||||
step 'I set the environment variables to:', table(
|
||||
%(
|
||||
| variable | value |
|
||||
| MM_HOSTSRC | .hosts |
|
||||
)
|
||||
)
|
||||
|
||||
step 'a file named ".hosts" with:', string
|
||||
end
|
||||
|
||||
Given /I start a dns server with:/ do |string|
|
||||
@dns_server.terminate if defined? @dns_server
|
||||
|
||||
port = 5300
|
||||
db_file = 'dns.db'
|
||||
|
||||
step 'I set the environment variables to:', table(
|
||||
%(
|
||||
| variable | value |
|
||||
| MM_DNSRC | 127.0.0.1:#{port}|
|
||||
)
|
||||
)
|
||||
|
||||
set_env 'PATH', File.expand_path(File.join(current_dir, 'bin')) + ':' + ENV['PATH']
|
||||
write_file db_file, string
|
||||
|
||||
@dns_server = run("dns_server.rb #{db_file} #{port}", 120)
|
||||
end
|
||||
|
||||
Given /I start a mdns server with:/ do |string|
|
||||
@mdns_server.terminate if defined? @mdns_server
|
||||
|
||||
port = 5301
|
||||
db_file = 'mdns.db'
|
||||
|
||||
step 'I set the environment variables to:', table(
|
||||
%(
|
||||
| variable | value |
|
||||
| MM_MDNSRC | 127.0.0.1:#{port}|
|
||||
)
|
||||
)
|
||||
|
||||
set_env 'PATH', File.expand_path(File.join(current_dir, 'bin')) + ':' + ENV['PATH']
|
||||
write_file db_file, string
|
||||
|
||||
@mdns_server = run("dns_server.rb #{db_file} #{port}", 120)
|
||||
end
|
||||
|
||||
Given /I start a mdns server for the local hostname/ do
|
||||
step %(I start a mdns server with:), "#{Socket.gethostname}: 127.0.0.1"
|
||||
end
|
||||
|
||||
# Make sure each and every process is really dead
|
||||
After do
|
||||
only_processes.each { |p| p.terminate }
|
||||
end
|
||||
|
||||
Before '@ruby-2.1' do
|
||||
skip_this_scenario if RUBY_VERSION < '2.1'
|
||||
end
|
|
@ -1,7 +1,5 @@
|
|||
# encoding: UTF-8
|
||||
|
||||
require 'rack/mock'
|
||||
require 'middleman-core/rack'
|
||||
require 'rspec/expectations'
|
||||
require 'capybara/cucumber'
|
||||
|
||||
Given /^a clean server$/ do
|
||||
@initialize_commands = []
|
||||
|
@ -54,7 +52,7 @@ Given /^the Server is running$/ do
|
|||
end
|
||||
|
||||
rack = ::Middleman::Rack.new(@server_inst)
|
||||
@browser = ::Rack::MockRequest.new(rack.to_app)
|
||||
Capybara.app = rack.to_app
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -69,76 +67,72 @@ end
|
|||
|
||||
When /^I go to "([^\"]*)"$/ do |url|
|
||||
cd(".") do
|
||||
@last_response = @browser.get(URI.encode(url))
|
||||
visit(URI.encode(url).to_s)
|
||||
end
|
||||
end
|
||||
|
||||
Then /^going to "([^\"]*)" should not raise an exception$/ do |url|
|
||||
cd(".") do
|
||||
last_response = nil
|
||||
expect {
|
||||
last_response = @browser.get(URI.encode(url))
|
||||
}.to_not raise_exception
|
||||
@last_response = last_response
|
||||
expect{ visit(URI.encode(url).to_s) }.to_not raise_exception
|
||||
end
|
||||
end
|
||||
|
||||
Then /^the content type should be "([^\"]*)"$/ do |expected|
|
||||
cd(".") do
|
||||
expect(@last_response.content_type).to start_with(expected)
|
||||
expect(page.response_headers['Content-Type']).to start_with expected
|
||||
end
|
||||
end
|
||||
|
||||
Then /^I should see "([^\"]*)"$/ do |expected|
|
||||
cd(".") do
|
||||
expect(@last_response.body).to include(expected)
|
||||
expect(page.body).to include expected
|
||||
end
|
||||
end
|
||||
|
||||
Then /^I should see '([^\']*)'$/ do |expected|
|
||||
cd(".") do
|
||||
expect(@last_response.body).to include(expected)
|
||||
expect(page.body).to include expected
|
||||
end
|
||||
end
|
||||
|
||||
Then /^I should see:$/ do |expected|
|
||||
cd(".") do
|
||||
expect(@last_response.body).to include(expected)
|
||||
expect(page.body).to include expected
|
||||
end
|
||||
end
|
||||
|
||||
Then /^I should not see "([^\"]*)"$/ do |expected|
|
||||
cd(".") do
|
||||
expect(@last_response.body).to_not include(expected)
|
||||
expect(page.body).not_to include expected
|
||||
end
|
||||
end
|
||||
|
||||
Then /^I should see content matching %r{(.*)}$/ do |expected|
|
||||
cd(".") do
|
||||
expect(@last_response.body).to match(expected)
|
||||
expect(page.body).to match(expected)
|
||||
end
|
||||
end
|
||||
|
||||
Then /^I should not see content matching %r{(.*)}$/ do |expected|
|
||||
cd(".") do
|
||||
expect(@last_response.body).to_not match(expected)
|
||||
expect(page.body).to_not match(expected)
|
||||
end
|
||||
end
|
||||
|
||||
Then /^I should not see:$/ do |expected|
|
||||
cd(".") do
|
||||
expect(@browser.last_response.body).to_not include(expected.chomp)
|
||||
expect(page.body).not_to include expected
|
||||
end
|
||||
end
|
||||
|
||||
Then /^the status code should be "([^\"]*)"$/ do |expected|
|
||||
cd(".") do
|
||||
expect(@browser.last_response.status).to eq expected.to_i
|
||||
expect(page.status_code).to eq expected.to_i
|
||||
end
|
||||
end
|
||||
|
||||
Then /^I should see "([^\"]*)" lines$/ do |lines|
|
||||
cd(".") do
|
||||
expect(@last_response.body.chomp.split($/).length).to eq(lines.to_i)
|
||||
expect(page.body.chomp.split($/).length).to eq lines.to_i
|
||||
end
|
||||
end
|
||||
|
|
|
@ -199,12 +199,12 @@ module Middleman
|
|||
# @param [Hash] options Data to pass through.
|
||||
# @return [String] The fully qualified asset url
|
||||
Contract IsA['Middleman::Application'], String, String, Hash => String
|
||||
def asset_url(app, path, prefix='', _options={})
|
||||
def asset_url(app, path, prefix='', options={})
|
||||
# Don't touch assets which already have a full path
|
||||
if path.include?('//') || path.start_with?('data:')
|
||||
if path.include?('//') || path.start_with?('data:') || !options[:current_resource]
|
||||
path
|
||||
else # rewrite paths to use their destination path
|
||||
if resource = app.sitemap.find_resource_by_destination_path(url_for(app, path))
|
||||
result = if resource = app.sitemap.find_resource_by_destination_path(url_for(app, path))
|
||||
resource.url
|
||||
else
|
||||
path = File.join(prefix, path)
|
||||
|
@ -214,6 +214,13 @@ module Middleman
|
|||
File.join(app.config[:http_prefix], path)
|
||||
end
|
||||
end
|
||||
|
||||
if options[:relative] != true
|
||||
result
|
||||
else
|
||||
current_dir = Pathname('/' + options[:current_resource].destination_path)
|
||||
Pathname(result).relative_path_from(current_dir.dirname).to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -369,6 +376,30 @@ module Middleman
|
|||
false
|
||||
end
|
||||
|
||||
# Glob a directory and try to keep path encoding consistent.
|
||||
#
|
||||
# @param [String] path The glob path.
|
||||
# @return [Array<String>]
|
||||
def glob_directory(path)
|
||||
results = ::Dir[path]
|
||||
|
||||
return results unless RUBY_PLATFORM =~ /darwin/
|
||||
|
||||
results.map { |r| r.encode('UTF-8', 'UTF-8-MAC') }
|
||||
end
|
||||
|
||||
# Get the PWD and try to keep path encoding consistent.
|
||||
#
|
||||
# @param [String] path The glob path.
|
||||
# @return [Array<String>]
|
||||
def current_directory
|
||||
result = ::Dir.pwd
|
||||
|
||||
return result unless RUBY_PLATFORM =~ /darwin/
|
||||
|
||||
result.encode('UTF-8', 'UTF-8-MAC')
|
||||
end
|
||||
|
||||
# Get a relative path to a resource.
|
||||
#
|
||||
# @param [Middleman::Sitemap::Resource] curr_resource The resource.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module Middleman
|
||||
# Current Version
|
||||
# @return [String]
|
||||
VERSION = '4.0.0.rc.1' unless const_defined?(:VERSION)
|
||||
VERSION = '4.0.0.rc.2' unless const_defined?(:VERSION)
|
||||
end
|
||||
|
|
|
@ -16,11 +16,10 @@ Gem::Specification.new do |s|
|
|||
s.files = `git ls-files -z`.split("\0")
|
||||
s.test_files = `git ls-files -z -- {fixtures,features}/*`.split("\0")
|
||||
s.require_path = 'lib'
|
||||
s.required_ruby_version = '>= 1.9.3'
|
||||
s.required_ruby_version = '>= 2.0.0'
|
||||
|
||||
# Core
|
||||
s.add_dependency('bundler', ['~> 1.1'])
|
||||
s.add_dependency('backports', ['~> 3.6'])
|
||||
s.add_dependency('rack', ['>= 1.4.5', '< 2.0'])
|
||||
s.add_dependency('tilt', ['~> 1.4.1'])
|
||||
s.add_dependency('erubis')
|
||||
|
@ -33,6 +32,9 @@ Gem::Specification.new do |s|
|
|||
# Watcher
|
||||
s.add_dependency('listen', ['~> 3.0'])
|
||||
|
||||
# Tests
|
||||
s.add_dependency("capybara", ["~> 2.4.4"])
|
||||
|
||||
# i18n
|
||||
s.add_dependency('i18n', ['~> 0.7.0'])
|
||||
|
||||
|
|
118
middleman-core/spec/middleman-core/dns_resolver_spec.rb
Normal file
118
middleman-core/spec/middleman-core/dns_resolver_spec.rb
Normal file
|
@ -0,0 +1,118 @@
|
|||
require 'spec_helper'
|
||||
require 'middleman-core/dns_resolver'
|
||||
|
||||
RSpec.describe Middleman::DnsResolver do
|
||||
subject(:resolver) do
|
||||
described_class.new(
|
||||
hosts_resolver: hosts_resolver,
|
||||
local_link_resolver: local_link_resolver,
|
||||
network_resolver: network_resolver
|
||||
)
|
||||
end
|
||||
|
||||
let(:hosts_resolver) { instance_double('Middleman::DnsResolver::HostsResolver') }
|
||||
let(:local_link_resolver) { instance_double('Middleman::DnsResolver::LocalLinkResolver') }
|
||||
let(:network_resolver) { instance_double('Middleman::DnsResolver::NetworkResolver') }
|
||||
|
||||
before :each do
|
||||
allow(network_resolver).to receive(:timeouts=)
|
||||
end
|
||||
|
||||
describe '#names_for' do
|
||||
context 'when hosts resolver can resolve name' do
|
||||
before :each do
|
||||
expect(hosts_resolver).to receive(:getnames).with(unresolved_ip).and_return(resolved_names)
|
||||
if RUBY_VERSION >= '2.1'
|
||||
expect(local_link_resolver).not_to receive(:getnames)
|
||||
end
|
||||
expect(network_resolver).not_to receive(:getnames)
|
||||
end
|
||||
|
||||
let(:unresolved_ip) { '127.0.0.1' }
|
||||
let(:resolved_names) { %w(localhost) }
|
||||
|
||||
it { expect(resolver.names_for(unresolved_ip)).to eq resolved_names }
|
||||
end
|
||||
|
||||
context 'when local link resolver can resolve name' do
|
||||
before :each do
|
||||
expect(hosts_resolver).to receive(:getnames).with(unresolved_ip).and_return([])
|
||||
if RUBY_VERSION >= '2.1'
|
||||
expect(local_link_resolver).to receive(:getnames).with(unresolved_ip).and_return(resolved_names)
|
||||
expect(network_resolver).not_to receive(:getnames)
|
||||
else
|
||||
expect(network_resolver).to receive(:getnames).with(unresolved_ip).and_return(resolved_names)
|
||||
end
|
||||
end
|
||||
|
||||
let(:unresolved_ip) { '127.0.0.1' }
|
||||
let(:resolved_names) { %w(localhost) }
|
||||
|
||||
it { expect(resolver.names_for(unresolved_ip)).to eq resolved_names }
|
||||
end
|
||||
|
||||
context 'when network resolver can resolve name' do
|
||||
before :each do
|
||||
expect(hosts_resolver).to receive(:getnames).with(unresolved_ip).and_return([])
|
||||
if RUBY_VERSION >= '2.1'
|
||||
expect(local_link_resolver).to receive(:getnames).with(unresolved_ip).and_return([])
|
||||
end
|
||||
expect(network_resolver).to receive(:getnames).with(unresolved_ip).and_return(resolved_names)
|
||||
end
|
||||
|
||||
let(:unresolved_ip) { '127.0.0.1' }
|
||||
let(:resolved_names) { %w(localhost) }
|
||||
|
||||
it { expect(resolver.names_for(unresolved_ip)).to eq resolved_names }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ips_for' do
|
||||
context 'when hosts resolver can resolve name' do
|
||||
before :each do
|
||||
expect(hosts_resolver).to receive(:getaddresses).with(unresolved_ips).and_return(resolved_name)
|
||||
if RUBY_VERSION >= '2.1'
|
||||
expect(local_link_resolver).not_to receive(:getaddresses)
|
||||
end
|
||||
expect(network_resolver).not_to receive(:getaddresses)
|
||||
end
|
||||
|
||||
let(:unresolved_ips) { '127.0.0.1' }
|
||||
let(:resolved_name) { %w(localhost) }
|
||||
|
||||
it { expect(resolver.ips_for(unresolved_ips)).to eq resolved_name }
|
||||
end
|
||||
|
||||
context 'when local link resolver can resolve name' do
|
||||
before :each do
|
||||
expect(hosts_resolver).to receive(:getaddresses).with(unresolved_ips).and_return([])
|
||||
if RUBY_VERSION >= '2.1'
|
||||
expect(local_link_resolver).to receive(:getaddresses).with(unresolved_ips).and_return(resolved_name)
|
||||
expect(network_resolver).not_to receive(:getaddresses)
|
||||
else
|
||||
expect(network_resolver).to receive(:getaddresses).with(unresolved_ips).and_return(resolved_name)
|
||||
end
|
||||
end
|
||||
|
||||
let(:unresolved_ips) { '127.0.0.1' }
|
||||
let(:resolved_name) { %w(localhost) }
|
||||
|
||||
it { expect(resolver.ips_for(unresolved_ips)).to eq resolved_name }
|
||||
end
|
||||
|
||||
context 'when network resolver can resolve name' do
|
||||
before :each do
|
||||
expect(hosts_resolver).to receive(:getaddresses).with(unresolved_ips).and_return([])
|
||||
if RUBY_VERSION >= '2.1'
|
||||
expect(local_link_resolver).to receive(:getaddresses).with(unresolved_ips).and_return([])
|
||||
end
|
||||
expect(network_resolver).to receive(:getaddresses).with(unresolved_ips).and_return(resolved_name)
|
||||
end
|
||||
|
||||
let(:unresolved_ips) { '127.0.0.1' }
|
||||
let(:resolved_name) { %w(localhost) }
|
||||
|
||||
it { expect(resolver.ips_for(unresolved_ips)).to eq resolved_name }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
require 'spec_helper'
|
||||
require 'middleman-core/preview_server/server_hostname'
|
||||
|
||||
RSpec.describe Middleman::PreviewServer::ServerHostname do
|
||||
subject(:hostname) { described_class.new(string) }
|
||||
let(:string) { 'www.example.com' }
|
||||
|
||||
describe '#to_s' do
|
||||
context 'when hostname' do
|
||||
it { expect(hostname.to_s).to eq string }
|
||||
end
|
||||
|
||||
context 'when ipv4' do
|
||||
let(:string) { '127.0.0.1' }
|
||||
it { expect(hostname.to_s).to eq string }
|
||||
end
|
||||
|
||||
context 'when ipv6' do
|
||||
let(:string) { '2607:f700:8000:12e:b3d9:1cba:b52:aa1b' }
|
||||
it { expect(hostname.to_s).to eq string }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_browser' do
|
||||
context 'when hostname' do
|
||||
it { expect(hostname.to_browser).to eq string }
|
||||
end
|
||||
|
||||
context 'when ipv4' do
|
||||
let(:string) { '127.0.0.1' }
|
||||
it { expect(hostname.to_browser).to eq string }
|
||||
end
|
||||
|
||||
context 'when ipv6' do
|
||||
let(:string) { '::1' }
|
||||
it { expect(hostname.to_browser).to eq string }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
require 'spec_helper'
|
||||
require 'middleman-core/preview_server/server_ip_address'
|
||||
|
||||
RSpec.describe Middleman::PreviewServer::ServerIpAddress do
|
||||
subject(:ip_address) { described_class.new(string) }
|
||||
let(:string) { '127.0.0.1' }
|
||||
|
||||
describe '#to_s' do
|
||||
context 'when ipv4' do
|
||||
let(:string) { '127.0.0.1' }
|
||||
it { expect(ip_address.to_s).to eq string }
|
||||
end
|
||||
|
||||
context 'when ipv6' do
|
||||
context 'without suffix' do
|
||||
let(:string) { '2607:f700:8000:12e:b3d9:1cba:b52:aa1b' }
|
||||
it { expect(ip_address.to_s).to eq string }
|
||||
end
|
||||
|
||||
context 'with suffix' do
|
||||
let(:string) { '2607:f700:8000:12e:b3d9:1cba:b52:aa1b%wlp1s0' }
|
||||
let(:result) { '2607:f700:8000:12e:b3d9:1cba:b52:aa1b' }
|
||||
it { expect(ip_address.to_s).to eq result }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_browser' do
|
||||
context 'when ip_address' do
|
||||
it { expect(ip_address.to_browser).to eq string }
|
||||
end
|
||||
|
||||
context 'when ipv4' do
|
||||
let(:string) { '127.0.0.1' }
|
||||
it { expect(ip_address.to_browser).to eq string }
|
||||
end
|
||||
|
||||
context 'when ipv6' do
|
||||
let(:string) { '2607:f700:8000:12e:b3d9:1cba:b52:aa1b' }
|
||||
it { expect(ip_address.to_browser).to eq "[#{string}]" }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,4 +5,30 @@ require 'coveralls'
|
|||
Coveralls.wear!
|
||||
|
||||
require 'codeclimate-test-reporter'
|
||||
CodeClimate::TestReporter.start
|
||||
CodeClimate::TestReporter.start
|
||||
|
||||
require 'aruba/api'
|
||||
RSpec.configure do |config|
|
||||
config.include Aruba::Api
|
||||
end
|
||||
|
||||
# encoding: utf-8
|
||||
RSpec.configure do |config|
|
||||
config.filter_run :focus
|
||||
config.run_all_when_everything_filtered = true
|
||||
|
||||
config.default_formatter = 'doc' if config.files_to_run.one?
|
||||
|
||||
# config.profile_examples = 10
|
||||
config.order = :random
|
||||
Kernel.srand config.seed
|
||||
|
||||
config.expect_with :rspec do |expectations|
|
||||
expectations.syntax = :expect
|
||||
end
|
||||
|
||||
config.mock_with :rspec do |mocks|
|
||||
mocks.syntax = :expect
|
||||
mocks.verify_partial_doubles = true
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue