Compare commits

...

55 Commits

Author SHA1 Message Date
Thomas Reynolds b6167b1369 Fix #2019 2016-12-03 11:32:09 -08:00
Thomas Reynolds 30217d2c04 Change how config options are passed to Thor. Removes new Thor warnings from #2017 2016-11-29 20:14:19 -08:00
Thomas Reynolds 073f273fe4 Prep 2016-11-25 14:03:56 -08:00
Thomas Reynolds e30f413376 Fix broken block ignore form. 2016-11-25 13:59:43 -08:00
Stanislav a680fb30c5 Add bower_components ignore (#2010) 2016-11-23 09:34:53 -08:00
Jonathan Allard 42e31c8c5e Make .html implicit for page IDs (#1996)
* Make .html implicit for page IDs

* Add Pry gem

* Add setting :page_id_generator to override page ID derivation
2016-11-16 21:00:16 -08:00
sandstrom 046d15cfa5 Fix addressable require (#2009) 2016-11-16 20:59:25 -08:00
Thomas Reynolds 5adea781c9 Fix up tests 2016-11-04 08:23:33 -07:00
James Pearson 97082d5fc4 Added addressable to the gemfile (#2001)
addressable is required for middleman init, but was not installed as part of a fresh install of Middleman
2016-11-04 07:56:30 -07:00
René Klačan 604c0e2b5d Fix source watcher configuration (#1999)
* Fix source watcher configuration

* Keep old Sources#initialize signature

* Poll source on path change
2016-10-31 10:50:03 -07:00
Mike Vastola 7c968b9572 Updates to support Rails 5, Rack 3 and Sinatra 2 (beta) (#1984)
* Updates to support Rails 5, Rack 3 and Sinatra 2 (beta).

- Bump upper boundary of version for Rack dependency from 2.0 to 3 (exclusive).
- Version bump sinatra for CI testing to >= 2.0.0.beta2.
- Also replaces use of String#hash in middleman-core/features/asset_host.feature
to ensure sufficiently random variables are returned.

Closes middleman/middleman#1983.

* Testing revert of the version requirements...

To confirm my changes don't cause a regression for any of:
- Rails < 5
- Sinatra < 2
- Rack < 2

* Revert "Testing revert of the version requirements..."

This reverts commit 5cf4c2a07c0814eefa573358b1bc9b0eeb62f9c1.
2016-09-13 10:25:17 +02:00
Kevin Glowacz 197093b36c Tilt 2 (#1974)
* Updates for mappings change in tilt 2

* drop RedcarpetTemplate::Redcarpet2 which was removed in tilt 2

* require Tilt 2.0 or higher

* Make use of tilt 2's extensions_for method instead
2016-09-09 00:18:34 +02:00
Kevin Glowacz ea2115f3f8 Allow for activesupport 4.2 again (#1976)
There is no current reason to remove support for 4.2
This now makes for a backwards compatible change whereas before this would
have called for a new major version of middleman-core
2016-08-30 17:22:31 -07:00
Thomas Reynolds 07651c63a6 Update Ruby version requirement. 2016-08-13 15:41:19 -07:00
Mark Rowe 65462cbc43 Improve performance of ignoring files (#1971)
* Determine the type of ignore pattern once per pattern.

Performing this work when checking when each file was ignored accounted
for around 4.5 of the 6 seconds that processing ignored files was taking
on a site with ~14,000 files and a small number of ignore patterns.
After this change, processing ignored files takes less than 1.5 seconds.

* Cache the normalized paths on Resource and SourceFile.

Normalizing the paths is expensive, so avoid doing it multiple
times. `Util::normalize_path` is marked as memoized, but this
is not effective as:

1. `memoize` doesn't appear to work with module functions.
2. Checking whether we have a memoized value is as expensive as
   normalizing the path.

This further drops the time it takes to process ignored files on a site
with ~14,000 files from ~1.5 seconds to ~0.6 seconds.
2016-08-12 16:33:18 -07:00
Eliott Appleford c264b05906 Merge pull request #1970 from bdash/source_watcher-poll_once-perf
Eliminate quadratic behavior in SourceWatcher::poll_once!
2016-08-11 10:02:38 +01:00
Mark Rowe dcd36a4f99 Eliminate quadratic behavior in `SourceWatcher::poll_once!`.
Both `Array#reject` and `Array#include?` have linear time
complexity. This results in quadratic time complexity when
`Array#include?` is called within `Array#reject`'s block. Using
`Array`'s difference operator gives the same result in linear time.

In a site with ~14,000 files, this drops the time taken by some calls to
`SourceWatcher::poll_once!` from ~15 seconds each to just a few
milliseconds.
2016-08-11 00:24:42 -07:00
Thomas Reynolds 7027b4933a Update .travis.yml 2016-08-03 13:00:25 -07:00
Peter Sankauskas cbad571338 Updating to activesupport version 5 (#1967) 2016-08-03 12:59:57 -07:00
sandstrom ffef14deb4 Create ISSUE_TEMPLATE.md (#1966) 2016-07-29 09:00:10 -07:00
Thomas Reynolds 62dba443a0 Fix japanese characters encoding in URLs 2016-07-10 17:03:22 -07:00
Adam Heath 389e3f5a8c Add prefix option to asset_hash (#1949)
This allows manually changing the filename so that fiel header changes
can be reflected on the CDN. E.g. if you turn on crossOrigin serving
(CORS) the asset hash doesn't change, but the CDN cache needs to be
broken in order to pickup the new header.
2016-07-02 17:55:25 -07:00
Thomas Reynolds ea39d16d81 Try to be safe 2016-06-23 12:41:23 -07:00
Peter Suschlik 1581bfc27e Convert `latency` option to a Fixnum (#1928)
As @vill pointed out in https://github.com/middleman/middleman/issues/1866#issuecomment-222869287
passing `--watcher-latency=2` fails because the gem `listen` expects `latency` to be an `Fixnum`.

This commit fixes this issue.
2016-06-01 14:19:13 -07:00
Thomas Reynolds e8f10fe3c2 prep 2016-05-31 13:15:37 -07:00
Thomas Reynolds 1efa585c11 Fix #1866 2016-05-31 13:15:37 -07:00
Shawn Van Ittersum 0d5c9e4313 Prevent overwrite of Slim embedded options (#1927)
* Fix middleman/middleman#1925: Slim embedded options overwrite

* Remove context_hack from Slim renderer

* Remove debugging output
2016-05-31 13:15:21 -07:00
Thomas Reynolds 51ccfe1143 only encode if current path is broken 2016-05-25 11:25:24 -07:00
Thomas Reynolds 4ffaa9dde5 Try to encode URI before parsing so spaces in paths work. Fixes #1914 2016-05-25 11:13:59 -07:00
Thomas Reynolds 6440d53e7a Add support for dotenv 2016-05-25 10:54:49 -07:00
Ben Hollis 38a9025560 Fix asset_url with asset_hash (#1919)
* Fix asset_url with asset_hash.

* Fix asset hashes.
2016-05-22 12:31:53 -07:00
Steven Sloan 5de9e86a55 allow partial lookups without a current_resource (#1912)
current_resource is only needed for relative lookups, so for lookups from source allow them to run without a current resource.
2016-05-13 13:01:21 -07:00
Thomas Reynolds ccc1cc1288 Fix ordering in tests 2016-05-11 11:48:21 -07:00
Thomas Reynolds 849fc65260 prep 2016-05-11 10:59:48 -07:00
Matthew Lehner 3e4187568b require the `try` core extension (#1911) 2016-05-10 15:17:28 -07:00
PiotrMisiurek 1c57626445 Fix contract for Sitemap::Store.register_resource_list_manipulator (#1907)
Contract said that optional param priority can be only a number.
But the code also handle the boolean as a value to be compatibile
with old versions

Change contract to accept both Num and Bool
2016-05-06 00:12:03 -07:00
Steven Sloan cf4d40caff loosen contract on Resource#source_file to Maybe[String] (#1906)
this could legitimately return nil if there is no file_descriptor, as would be the case for a StringResource
2016-05-05 09:08:29 -07:00
Dennis Günnewig 599cf1e6d3 Use https:// to clone templates (#1901)
Using git://-protocol for cloning git repositories does not work via
HTTP proxies. By replacing it with https:// instead it works for more
users.
2016-04-29 14:23:55 -07:00
Thomas Reynolds 3ebd902ec3 Move capybara dep 2016-04-28 15:44:50 -07:00
Thomas Reynolds 158c3e9f25 Test new ruby 2016-04-28 08:47:48 -07:00
Thomas Reynolds cdcd2bd42d Expose development? and production? helpers to template context. Related #1895 2016-04-26 09:24:49 -07:00
Thomas Bruketta 4918704800 Allow numbers to be unique page_ids (#1886) 2016-04-25 09:51:11 -07:00
Nick Giancola 8a8ee768ac Fix/issue 1889 (#1892)
* Add regression test for infinite loop issue in Util::step_through_extensions (#1889)

* Prevent infinite loop when encountering files where base filename is a possible templating engine
2016-04-25 09:50:51 -07:00
Thomas Reynolds 71a20bb3ee Merge pull request #1891 from danielbayerlein/incompatibility-of-listen
Incompatibility of listen with Ruby < 2.2
2016-04-24 16:59:07 -07:00
Daniel Bayerlein ce58073539 Incompatibility of listen with Ruby < 2.2 fixed 2016-04-24 09:57:13 +02:00
Thomas Reynolds 13d62cb276 Experiment with non-rack rewriters 2016-04-22 15:52:42 -07:00
Thomas Reynolds 0f2bc1e0ea Minor perf tweaks 2016-04-21 16:12:33 -07:00
Thomas Reynolds a14934e08b Merge pull request #1888 from mortonfox/patch-1
Fix RubyInstaller Devkit link.
2016-04-20 14:22:42 -07:00
Morton Fox a95f721490 Fix RubyInstaller Devkit link. 2016-04-20 00:27:15 -04:00
Thomas Reynolds 2b0f720850 Bad merge 2016-04-19 11:54:00 -07:00
Thomas Reynolds b386dcdc40 Fix #1884 for realz 2016-04-19 11:08:23 -07:00
Thomas Reynolds 0ac5650229 Throw when trying to overwrite a template context value. Fixes #1884 2016-04-19 11:06:51 -07:00
Thomas Reynolds 7e3baed196 Throw when trying to overwrite a template context value. Fixes #1884 2016-04-19 10:37:23 -07:00
Thomas Reynolds 09a7d89fd3 Throw when trying to overwrite a template context value. Fixes #1884 2016-04-19 10:36:49 -07:00
Thomas Reynolds 6872e07d34 Expose extensions in config. Helps middleman/middleman-sprockets#111 2016-04-17 13:02:23 -07:00
70 changed files with 924 additions and 275 deletions

View File

@ -67,3 +67,5 @@ Style/MultilineBlockChain:
Enabled: false
Style/SpecialGlobalVars:
Enabled: false
Style/FrozenStringLiteralComment:
Enabled: false

View File

@ -5,17 +5,14 @@ before_script:
- bundle update
rvm:
- ruby-head
- 2.3.0
- 2.3.1
- 2.2.4
- 2.1
- 2.0
os:
- linux
# - osx
matrix:
fast_finish: true
allow_failures:
- rvm: 2.3.0
- rvm: ruby-head
env:
global:

View File

@ -1,9 +1,41 @@
master
===
# 4.1.13
* Change how config options are passed to Thor. Removes new Thor warnings from #2017
# 4.1.12
* Fix broken `ignore { |p| true }` form.
# 4.1.11
* Upgrade to Rack 2.
# 4.1.10
* Fix unicode issues in URL deeplinks.
* Add prefix option to asset_hash (#1949)
# 4.1.9
* Fix `--watcher-*` CLI flags.
* Allow spaces in paths to work with `link_to`. Fixes #1914
* Add support for dotenv
* Fix asset_url with asset_hash (#1919)
* Allow partial lookups without a current_resource (#1912)
# 4.1.8
* Expose `development?` and `production?` helpers to template context.
* require the `try` core extension (#1911)
* Fix contract for Sitemap::Store.register_resource_list_manipulator (#1907)
* Loosen contract on Resource#source_file to Maybe[String] (#1906)
* Let collection loops access ConfigContext for helpers. #1879
* Use https:// to clone templates (#1901)
* Allow numbers to be unique page_ids (#1886)
* Prevent infinite loop when encountering files where base filename is a possible templating engine
# 4.1.7

10
Gemfile
View File

@ -9,6 +9,12 @@ gem 'byebug'
gem 'aruba', '~> 0.7.4', require: false
gem 'rspec', '~> 3.0', require: false
gem 'cucumber', '~> 2.0', require: false
gem 'addressable', '~> 2.4.0', require: false
# Pry tools
gem 'pry'
gem 'pry-stack_explorer'
gem 'pry-rescue'
# Optional middleman dependencies, included for tests
gem 'haml', '>= 4.0.5', require: false
@ -18,7 +24,7 @@ gem 'kramdown', '~> 1.2', require: false
gem 'slim', '>= 2.0', require: false
gem 'liquid', '>= 2.6', require: false
gem 'stylus', '>= 1.0', require: false
gem 'sinatra', '>= 1.4', require: false
gem 'sinatra', '>= 2.0.0.beta2', require: false
gem 'redcarpet', '>= 3.1', require: false
# Dns server to test preview server
@ -26,7 +32,7 @@ gem 'rubydns', '~> 1.0.1', require: false
# To test javascript
gem 'poltergeist', '~> 1.8', require: false
gem 'phantomjs', '~> 1.9.8.0', require: false
gem 'phantomjs', '~> 2.1.1.0', require: false
# For less, note there is no compatible JS runtime for windows
gem 'therubyrhino', '>= 2.0', platforms: :jruby

9
ISSUE_TEMPLATE.md Normal file
View File

@ -0,0 +1,9 @@
## Expected behavior and actual behavior
## Steps to reproduce the problem (from a clean middleman installation)
## Additional information
- Ruby version:
- Middleman version:
- OS version:

View File

@ -121,6 +121,6 @@ Copyright (c) 2010-2015 Thomas Reynolds. MIT Licensed, see [LICENSE] for details
[codeclimate]: https://codeclimate.com/github/middleman/middleman
[gittip]: https://www.gittip.com/middleman/
[rubyinstaller]: http://rubyinstaller.org/
[RubyInstaller-Devkit]: http:rubyinstaller.org/add-ons/devkit/
[RubyInstaller-Devkit]: http://rubyinstaller.org/add-ons/devkit/
[rubydoc]: http://rubydoc.info/github/middleman/middleman
[LICENSE]: https://github.com/middleman/middleman/blob/master/LICENSE.md

View File

@ -9,6 +9,9 @@ end
require "middleman-core/load_paths"
Middleman.setup_load_paths
require 'dotenv'
::Dotenv.load
require 'middleman-core'
require 'middleman-core/logger'
@ -22,12 +25,10 @@ module Middleman::Cli
if setting.default.is_a?(String) || setting.default.is_a?(NilClass)
base.class_option setting.key,
type: :string,
default: :undefined,
desc: setting.description
elsif setting.default.is_a?(TrueClass) || setting.default.is_a?(FalseClass)
base.class_option setting.key,
type: :boolean,
default: :undefined,
desc: setting.description
end
end
@ -46,6 +47,8 @@ end
::Middleman::Logger.singleton(3)
::Middleman::Cli.config = ::Middleman::Application.new do
#
config[:environment] = (ENV['MM_ENV'] || ENV['RACK_ENV'] || 'development').to_sym
config[:mode] = :config
config[:exit_before_ready] = true
config[:watcher_disable] = true

View File

@ -10,7 +10,7 @@ module Middleman::Cli
class_option :environment,
aliases: '-e',
default: :production
default: ENV['MM_ENV'] || ENV['RACK_ENV'] || :production
class_option :clean,
type: :boolean,
default: true,
@ -29,7 +29,7 @@ module Middleman::Cli
default: false,
desc: 'Print debug messages'
class_option :instrument,
type: :string,
type: :boolean,
default: false,
desc: 'Print instrument messages'
class_option :profile,
@ -64,7 +64,7 @@ module Middleman::Cli
config[:mode] = :build
config[:show_exceptions] = false
config[:cli_options] = cli_options.each_with_object({}) do |(k, v), sum|
sum[k] = v unless v == :undefined
sum[k] = v
end
end

View File

@ -113,7 +113,7 @@ module Middleman::Cli
end
def repository_path(repo)
repo.include?('://') || repo.include?('git@') ? repo : "git://github.com/#{repo}.git"
repo.include?('://') || repo.include?('git@') ? repo : "https://github.com/#{repo}.git"
end
# Add to CLI

View File

@ -17,7 +17,7 @@ module Middleman::Cli
default: false,
desc: 'Print debug messages'
class_option :instrument,
type: :string,
type: :boolean,
default: false,
desc: 'Print instrument messages'
class_option :profile,

View File

@ -17,7 +17,7 @@ Gem::Specification.new do |s|
s.test_files = `git ls-files -z -- {fixtures,features}/*`.split("\0")
s.executable = 'middleman'
s.require_path = 'lib'
s.required_ruby_version = '>= 2.0.0'
s.required_ruby_version = '>= 2.2.0'
# CLI
s.add_dependency('thor', ['>= 0.17.0', '< 2.0'])

View File

@ -308,3 +308,17 @@ Feature: Assets get file hashes appended to them and references to them are upda
| javascripts/application.js.map |
And the file "javascripts/application-4553338c.js" should contain "//# sourceMappingURL=application.js-22cc2b5f.map"
Scenario: Hashes can contain a prefix
Given a successfully built app at "asset-hash-prefix"
When I cd to "build"
Then the following files should exist:
| index.html |
| javascripts/application-myprefix-4553338c.js |
| javascripts/application.js-myprefix-22cc2b5f.map |
| index.html |
And the following files should not exist:
| javascripts/application.js |
| javascripts/application.js.map |
And the file "javascripts/application-myprefix-4553338c.js" should contain "//# sourceMappingURL=application.js-myprefix-22cc2b5f.map"

View File

@ -25,7 +25,8 @@ Feature: Alternate between multiple asset hosts
And a file named "config.rb" with:
"""
activate :asset_host, host: Proc.new { |asset|
"http://assets%d.example.com" % (asset.hash % 4)
hash = Digest::MD5.digest(asset).bytes.map!(&:ord).reduce(&:+)
"http://assets%d.example.com" % (hash % 4)
}
"""
And the Server is running

View File

@ -40,19 +40,29 @@ Feature: link_to helper
"""
absolute: <%= link_to "Needs Index", "/needs_index.html", relative: true %>
relative: <%= link_to "Relative", "needs_index.html", relative: true %>
absolute spaces: <%= link_to "Spaces Index", "/evil spaces.html", relative: true %>
relative spaces: <%= link_to "Spaces Relative", "evil spaces.html", relative: true %>
"""
And a file named "source/link_to/sub.html.erb" with:
"""
absolute: <%= link_to "Needs Index", "/needs_index.html", relative: true %>
relative: <%= link_to "Relative", "../needs_index.html", relative: true %>
absolute spaces: <%= link_to "Spaces Index", "/evil spaces.html", relative: true %>
relative spaces: <%= link_to "Spaces Relative", "../evil spaces.html", relative: true %>
"""
And the Server is running at "indexable-app"
When I go to "/link_to.html"
Then I should see 'absolute: <a href="needs_index.html">Needs Index</a>'
Then I should see 'relative: <a href="needs_index.html">Relative</a>'
Then I should see 'absolute spaces: <a href="evil%20spaces.html">Spaces Index</a>'
Then I should see 'relative spaces: <a href="evil%20spaces.html">Spaces Relative</a>'
When I go to "/link_to/sub.html"
Then I should see 'absolute: <a href="../needs_index.html">Needs Index</a>'
Then I should see 'relative: <a href="../needs_index.html">Relative</a>'
Then I should see 'absolute spaces: <a href="../evil%20spaces.html">Spaces Index</a>'
Then I should see 'relative spaces: <a href="../evil%20spaces.html">Spaces Relative</a>'
Scenario: link_to relative works with strip_index_file
Given a fixture app "indexable-app"
@ -113,7 +123,7 @@ Feature: link_to helper
When I go to "/link_to/sub.html"
Then I should see 'absolute: <a href="../needs_index.html">Needs Index</a>'
Then I should see 'relative: <a href="../needs_index.html">Relative</a>'
Scenario: link_to knows about directory indexes
Given a fixture app "indexable-app"
And a file named "source/link_to.html.erb" with:

View File

@ -4,7 +4,7 @@ Feature: i18n Paths
Given a fixture app "empty-app"
And a file named "data/pages.yml" with:
"""
- hello.html
- hello.html
"""
And a file named "locales/en.yml" with:
"""
@ -50,24 +50,24 @@ Feature: i18n Paths
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>'
Then I should see '<a href="/index.html" class="current">Current Home</a>'
Then I should see '<a href="/es/index.html" title="Other Home">Other Home</a>'
Then I should see '<a href="/index.html" class="current"><span>Home: Current Block</span></a>'
Then I should see '<a href="/es/index.html" title="Other Home"><span>Home: Other Block</span></a>'
Then I should see '<a href="/hello.html" class="current">Current hello.html</a>'
Then I should see '<a href="/es/hola.html" title="Other hello.html">Other hello.html</a>'
Then I should see '<a href="/hello.html" class="current"><span>Current Block</span></a>'
Then I should see '<a href="/es/hola.html" title="Other hello.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>'
Then I should see '<a href="/es/index.html" class="current">Current Home</a>'
Then I should see '<a href="/index.html" title="Other Home">Other Home</a>'
Then I should see '<a href="/es/index.html" class="current"><span>Home: Current Block</span></a>'
Then I should see '<a href="/index.html" title="Other Home"><span>Home: Other Block</span></a>'
Then I should see '<a href="/es/hola.html" class="current">Current hello.html</a>'
Then I should see '<a href="/hello.html" title="Other hello.html">Other hello.html</a>'
Then I should see '<a href="/es/hola.html" class="current"><span>Current Block</span></a>'
Then I should see '<a href="/hello.html" title="Other hello.html"><span>Other Block</span></a>'
Scenario: link_to is i18n aware and supports relative_links
Given a fixture app "empty-app"
@ -124,30 +124,30 @@ Feature: i18n Paths
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>'
Then I should see '<a href="index.html" class="current">Current Home</a>'
Then I should see '<a href="es/index.html" title="Other Home">Other Home</a>'
Then I should see '<a href="index.html" class="current"><span>Home: Current Block</span></a>'
Then I should see '<a href="es/index.html" title="Other Home"><span>Home: Other Block</span></a>'
Then I should see '<a href="hello.html" class="current">Current hello.html</a>'
Then I should see '<a href="es/hola.html" title="Other hello.html">Other hello.html</a>'
Then I should see '<a href="hello.html" class="current"><span>Current Block</span></a>'
Then I should see '<a href="es/hola.html" title="Other hello.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>'
Then I should see '<a href="index.html" class="current">Current Home</a>'
Then I should see '<a href="../index.html" title="Other Home">Other Home</a>'
Then I should see '<a href="index.html" class="current"><span>Home: Current Block</span></a>'
Then I should see '<a href="../index.html" title="Other Home"><span>Home: Other Block</span></a>'
Then I should see '<a href="hola.html" class="current">Current hello.html</a>'
Then I should see '<a href="../hello.html" title="Other hello.html">Other hello.html</a>'
Then I should see '<a href="hola.html" class="current"><span>Current Block</span></a>'
Then I should see '<a href="../hello.html" title="Other 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
- hello.html
- article.html
"""
And a file named "locales/en.yml" with:

View File

@ -0,0 +1,41 @@
Feature: Markdown support in Slim
In order to test support of the Slim markdown filter
Scenario: Markdown filter in Slim works
Given a fixture app "markdown-in-slim-app"
And a file named "config.rb" with:
"""
set :markdown_engine, :redcarpet
activate :directory_indexes
"""
And a file named "source/markdown_filter.html.slim" with:
"""
markdown:
# H1
paragraph
"""
Given the Server is running at "markdown-in-slim-app"
When I go to "/markdown_filter/"
Then I should see ">H1</h1>"
Then I should see "<p>paragraph</p>"
Scenario: Markdown filter in Slim uses our link_to and image_tag helpers
Given a fixture app "markdown-in-slim-app"
And a file named "config.rb" with:
"""
set :markdown_engine, :redcarpet
activate :directory_indexes
"""
And a file named "source/link_and_image.html.slim" with:
"""
markdown:
[A link](/link_target.html)
![image](blank.gif)
"""
Given the Server is running at "markdown-in-slim-app"
When I go to "/link_and_image/"
Then I should see "/link_target/"
Then I should see 'src="/images/blank.gif"'

View File

@ -3,14 +3,60 @@ Feature: Page IDs
Scenario: link_to works with blocks (erb)
Given the Server is running at "page-id-app"
When I go to "/index.html"
Then I should see "I am: index.html"
Then I should see "I am: index"
And I should see "URL1: /fm.html"
And I should see "URL2: /2.html"
And I should see 'URL3: <a href="/3.html">Hi</a>'
And I should see 'URL4: <a href="/overwrites/from-default.html">Sym</a>'
And I should see 'URL5: <a href="/implicit.html">Imp</a>'
And I should see 'URL6: <a href="/folder/foldern.html">Foldern</a>'
And I should see 'URL7: <a href="/feed.xml">Feed</a>'
When I go to "/fm.html"
Then I should see "I am: frontmatter"
When I go to "/implicit.html"
Then I should see "I am: implicit"
When I go to "/feed.xml"
Then I should see "I am: feed.xml"
When I go to "/folder/foldern.html"
Then I should see "I am: folder/foldern"
When I go to "/1.html"
Then I should see "I am: page1"
When I go to "/2.html"
Then I should see "I am: page2"
When I go to "/3.html"
Then I should see "I am: page3"
When I go to "/overwrites/from-default.html"
Then I should see "I am: something-else"
When I go to "/overwrites/from-frontmatter.html"
Then I should see "I am: from_frontmatter"
Scenario: Override page ID derivation with a proc
Given a fixture app "page-id-app"
And app "page-id-app" is using config "proc"
And the Server is running at "page-id-app"
When I go to "/index.html"
Then I should see "I am: index.html-foo"
And I should see "URL1: /fm.html"
And I should see "URL2: /2.html"
And I should see 'URL3: <a href="/3.html">Hi</a>'
And I should see 'URL4: <a href="/overwrites/from-default.html">Sym</a>'
And I should see 'URL8: <a href="/implicit.html">Imp</a>'
And I should see 'URL9: <a href="/folder/foldern.html">Foldern</a>'
And I should see 'URL10: <a href="/feed.xml">Feed</a>'
When I go to "/fm.html"
Then I should see "I am: frontmatter"
When I go to "/implicit.html"
Then I should see "I am: implicit.html-foo"
When I go to "/feed.xml"
Then I should see "I am: feed.xml-foo"
When I go to "/folder/foldern.html"
Then I should see "I am: folder/foldern.html-foo"
When I go to "/1.html"
Then I should see "I am: page1"

View File

@ -120,7 +120,7 @@ Feature: Relative Assets
"""
And the Server is running at "relative-assets-app"
When I go to "/sub/image_tag.html"
Then I should see '<img src="../img/blank.gif" />'
Then I should see '<img src="../img/blank.gif"'
Scenario: Relative assets should not break data URIs in image_tag
Given a fixture app "relative-assets-app"

View File

@ -0,0 +1,123 @@
Feature: Relative Assets (Helpers Only)
Scenario: Rendering css with the feature enabled
Given a fixture app "relative-assets-app"
And a file named "config.rb" with:
"""
activate :relative_assets, helpers_only: true
"""
And a file named "source/stylesheets/relative_assets.css.sass.erb" with:
"""
h1
background: url("<%= asset_url('images/blank.gif') %>")
h2
background: url("<%= asset_url('/images/blank2.gif') %>")
"""
And a file named "source/javascripts/application.js.erb" with:
"""
function foo() {
var img = document.createElement('img');
img.src = '<%= asset_url("images/100px.jpg") %>';
var body = document.getElementsByTagName('body')[0];
body.insertBefore(img, body.firstChild);
}
window.onload = foo;
"""
And a file named "source/stylesheets/fonts3.css.erb" with:
"""
@font-face {
font-family: 'Roboto2';
src: url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.eot") %>);
src: url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.eot?#iefix") %>) format('embedded-opentype'),
url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.woff") %>) format('woff'),
url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.ttf") %>) format('truetype'),
url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.svg#robotoregular") %>) format('svg');
font-weight: normal;
font-style: normal;
}
"""
And the Server is running at "relative-assets-app"
When I go to "/stylesheets/relative_assets.css"
Then I should see 'url("../images/blank.gif'
And I should see 'url("../images/blank2.gif'
When I go to "/javascripts/application.js"
Then I should not see "../"
When I go to "/stylesheets/fonts3.css"
Then I should see 'url(../fonts/roboto/roboto-regular-webfont.eot'
And I should see 'url(../fonts/roboto/roboto-regular-webfont.woff'
And I should see 'url(../fonts/roboto/roboto-regular-webfont.ttf'
And I should see 'url(../fonts/roboto/roboto-regular-webfont.svg'
Scenario: Relative css reference with directory indexes
Given a fixture app "relative-assets-app"
And a file named "config.rb" with:
"""
activate :directory_indexes
activate :relative_assets, helpers_only: true
"""
And the Server is running at "relative-assets-app"
When I go to "/relative_image/index.html"
Then I should see "../stylesheets/relative_assets.css"
Scenario: Relative assets via image_tag
Given a fixture app "relative-assets-app"
And a file named "config.rb" with:
"""
activate :relative_assets, helpers_only: true
"""
And a file named "source/sub/image_tag.html.erb" with:
"""
<%= image_tag '/img/blank.gif' %>
"""
And the Server is running at "relative-assets-app"
When I go to "/sub/image_tag.html"
Then I should see '<img src="../img/blank.gif"'
Scenario: Relative assets should not break data URIs in image_tag
Given a fixture app "relative-assets-app"
And a file named "config.rb" with:
"""
activate :relative_assets, helpers_only: true
"""
And a file named "source/sub/image_tag.html.erb" with:
"""
<%= image_tag "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" %>
"""
And the Server is running at "relative-assets-app"
When I go to "/sub/image_tag.html"
Then I should see '<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" />'
Scenario: URLs are not rewritten for rewrite ignored paths
Given a fixture app "relative-assets-app"
And a file named "config.rb" with:
"""
activate :relative_assets, rewrite_ignore: [
'/stylesheets/fonts3.css',
], helpers_only: true
"""
And a file named "source/stylesheets/relative_assets.css.sass.erb" with:
"""
h1
background: url("<%= asset_url('images/blank.gif') %>")
h2
background: url("<%= asset_url('/images/blank2.gif') %>")
"""
And a file named "source/stylesheets/fonts3.css.erb" with:
"""
@font-face {
font-family: 'Roboto2';
src: url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.eot") %>);
src: url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.eot?#iefix") %>) format('embedded-opentype'),
url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.woff") %>) format('woff'),
url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.ttf") %>) format('truetype'),
url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.svg#robotoregular") %>) format('svg');
font-weight: normal;
font-style: normal;
}
"""
And the Server is running at "relative-assets-app"
When I go to "/stylesheets/relative_assets.css"
Then I should see 'url("../images/blank.gif'
When I go to "/stylesheets/fonts3.css"
Then I should see 'url(/fonts/roboto/roboto-regular-webfont.eot'

View File

@ -0,0 +1,40 @@
Feature: Sass/SCSS support in Slim
In order to test support of the Slim sass and scss filters
Scenario: Sass filter in Slim works
Given a fixture app "sass-in-slim-app"
And a file named "config.rb" with:
"""
activate :directory_indexes
"""
And a file named "source/sass_filter.html.slim" with:
"""
sass:
.sass
margin: 0
"""
Given the Server is running at "sass-in-slim-app"
When I go to "/sass_filter/"
Then I should see "text/css"
Then I should see ".sass"
Then I should see "margin:0"
Scenario: SCSS filter in Slim works
Given a fixture app "sass-in-slim-app"
And a file named "config.rb" with:
"""
activate :directory_indexes
"""
And a file named "source/scss_filter.html.slim" with:
"""
scss:
.scss {
margin: 0;
}
"""
Given the Server is running at "sass-in-slim-app"
When I go to "/scss_filter/"
Then I should see "text/css"
Then I should see ".scss"
Then I should see "margin:0"

View File

@ -0,0 +1,26 @@
Feature: Don't allow template locals to overwrite template helpers
Scenario: Normal Template
Given an empty app
And a file named "config.rb" with:
"""
class TestExt < ::Middleman::Extension
expose_to_template foo: :foo
def foo
"bar"
end
end
::Middleman::Extensions.register :test, TestExt
activate :test
page "/index.html", locals: { foo: false }
"""
And a file named "source/index.html.erb" with:
"""
<%= foo %>
"""
Given a built app at "empty_app"
Then the exit status should be 1

View File

@ -0,0 +1,7 @@
activate :asset_hash,
prefix: "myprefix-"
activate :relative_assets
activate :directory_indexes

View File

@ -0,0 +1,16 @@
class Middleware
def initialize(app)
@app = app
end
def call(env)
status, headers, response = @app.call(env)
body = ''
response.each {|part| body += part }
if (env["PATH_INFO"] =~ /css$/)
body += "\n/* Added by Rack filter */"
status, headers, response = Rack::Response.new(body, status, headers).finish
end
[status, headers, response]
end
end

View File

@ -0,0 +1,6 @@
<% content_for :head do %>
<title>The Middleman!</title>
<% end %>
<h1>Testing the sitemap hashing</h1>
<br /><br /><br />

View File

@ -0,0 +1,2 @@
function foo(){var message="HEY THERE FRIEND!";var para=document.createElement("p");para.innerHTML=message;var body=document.getElementsByTagName("body")[0];body.insertBefore(para,body.firstChild)}window.onload=foo;
//# sourceMappingURL=application.js.map

View File

@ -0,0 +1 @@
{"version":3,"sources":["source/javascripts/application.js"],"names":["foo","message","para","document","createElement","innerHTML","body","getElementsByTagName","insertBefore","firstChild","window","onload"],"mappings":"AAAA,QAASA,OACP,GAAIC,SAAU,mBACd,IAAIC,MAAOC,SAASC,cAAc,IAClCF,MAAKG,UAAYJ,OACb,IAAIK,MAAOH,SAASI,qBAAqB,QAAQ,EAC/CD,MAAKE,aAAaN,KAAMI,KAAKG,YAGpCC,OAAOC,OAASX"}

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<%= javascript_include_tag "application" %>
<%= yield_content :head %>
</head>
<body class="<%= page_classes %>">
<div id="main" role="main">
<%= yield %>
</div>
</body>
</html>

View File

@ -0,0 +1,7 @@
%w(1 2 3).each do |n|
proxy "/#{n}.html", "/index.html", id: "page#{n}"
end
page "/overwrites/*", id: :"something-else"
config[:page_id_generator] = ->(path){ path + "-foo" }

View File

@ -0,0 +1 @@
I am: <%= current_resource.page_id %>

View File

@ -0,0 +1 @@
I am: <%= current_resource.page_id %>

View File

@ -0,0 +1 @@
I am: <%= current_resource.page_id %>

View File

@ -4,3 +4,11 @@ URL1: <%= url_for "frontmatter" %>
URL2: <%= url_for "page2" %>
URL3: <%= link_to "Hi", "page3" %>
URL4: <%= link_to "Sym", :"something-else" %>
URL5: <%= link_to "Imp", :implicit %>
URL6: <%= link_to "Foldern", "folder/foldern" %>
URL7: <%= link_to "Feed", "feed.xml" %>
<%# If custom proc %>
URL8: <%= link_to "Imp", "implicit.html-foo" %>
URL9: <%= link_to "Foldern", "folder/foldern.html-foo" %>
URL10: <%= link_to "Feed", "feed.xml-foo" %>

View File

@ -203,6 +203,7 @@ module Middleman
define_setting :watcher_disable, false, 'If the Listen watcher should not run'
define_setting :watcher_force_polling, false, 'If the Listen watcher should run in polling mode'
define_setting :watcher_latency, nil, 'The Listen watcher latency'
define_setting :watcher_wait_for_delay, 0.5, 'The Listen watcher delay between calls when changes exist'
# Delegate convenience methods off to their implementations
def_delegator :"::Middleman::Logger", :singleton, :logger
@ -266,6 +267,8 @@ module Middleman
# Evaluate a passed block if given
config_context.instance_exec(&block) if block_given?
apply_cli_options
execute_callbacks(:before_sitemap)
# Initialize the Sitemap
@ -276,8 +279,6 @@ module Middleman
# Before config is parsed, before extensions get to it.
execute_callbacks(:initialized)
apply_cli_options
# Before config is parsed. Mostly used for extensions.
execute_callbacks(:before_configuration)
@ -344,11 +345,11 @@ module Middleman
# Clean up missing Tilt exts
def prune_tilt_templates!
::Tilt.mappings.each_key do |key|
::Tilt.default_mapping.lazy_map.each_key do |key|
begin
::Tilt[".#{key}"]
rescue LoadError, NameError
::Tilt.mappings.delete(key)
::Tilt.default_mapping.lazy_map.delete(key)
end
end
end

View File

@ -8,7 +8,7 @@ module Middleman
attr_reader :app
# Whitelist methods that can reach out.
def_delegators :@app, :config, :logger, :use, :map, :mime_type, :files, :root, :build?, :server?, :environment?
def_delegators :@app, :config, :logger, :use, :map, :mime_type, :files, :root, :build?, :server?, :environment?, :extensions
def_delegator :"@app.extensions", :activate
def initialize(app, template_context_class)
@ -23,7 +23,7 @@ module Middleman
end
def include(mod)
self.extend(mod)
extend(mod)
end
def helpers(*helper_modules, &block)

View File

@ -1,4 +1,5 @@
require 'padrino-helpers'
require 'middleman-core/contracts'
# Don't fail on invalid locale, that's not what our current
# users expect.

View File

@ -28,10 +28,7 @@ module Middleman
super
# Setup source collection.
@sources = ::Middleman::Sources.new(app,
disable_watcher: app.config[:watcher_disable],
force_polling: app.config[:watcher_force_polling],
latency: app.config[:watcher_latency])
@sources = ::Middleman::Sources.new(app)
# Add default ignores.
IGNORES.each do |key, value|
@ -55,6 +52,13 @@ module Middleman
# @return [void]
Contract Any
def after_configuration
@watcher.update_config(
disable_watcher: app.config[:watcher_disable],
force_polling: app.config[:watcher_force_polling],
latency: app.config[:watcher_latency],
wait_for_delay: app.config[:watcher_wait_for_delay]
)
if @original_source_dir != app.config[:source]
@watcher.update_path(app.config[:source])
end

View File

@ -73,18 +73,20 @@ module Middleman::CoreExtensions
return [{}, nil] unless file
return @cache[file[:full_path]] if @cache.key?(file[:full_path])
file_path = file[:full_path].to_s
@cache[file[:full_path]] = ::Middleman::Util::Data.parse(
file,
app.config[:frontmatter_delims]
)
@cache[file_path] ||= begin
::Middleman::Util::Data.parse(
file,
app.config[:frontmatter_delims]
)
end
end
Contract ArrayOf[IsA['Middleman::SourceFile']], ArrayOf[IsA['Middleman::SourceFile']] => Any
def clear_data(updated_files, removed_files)
(updated_files + removed_files).each do |file|
@cache.delete(file[:full_path])
@cache.delete(file[:full_path].to_s)
end
end
end

View File

@ -1,6 +1,6 @@
require 'rack'
require 'rack/response'
require 'addressable/uri'
require 'memoist'
require 'middleman-core/util'
require 'middleman-core/contracts'
@ -11,13 +11,12 @@ module Middleman
expose_to_application rewrite_inline_urls: :add
IGNORE_DESCRIPTOR = Or[Regexp, RespondTo[:call], String]
REWRITER_DESCRIPTOR = {
id: Symbol,
proc: Or[Proc, Method],
url_extensions: ArrayOf[String],
source_extensions: ArrayOf[String],
ignore: ArrayOf[IGNORE_DESCRIPTOR],
ignore: ArrayOf[::Middleman::Util::IGNORE_DESCRIPTOR],
after: Maybe[Symbol]
}.freeze
@ -33,6 +32,8 @@ module Middleman
end
def after_configuration
return if @rewriters.empty?
rewriters = @rewriters.values.sort do |a, b|
if b[:after] && b[:after] == a[:id]
1
@ -45,6 +46,7 @@ module Middleman
end
class Rack
extend Memoist
include Contracts
Contract RespondTo[:call], {
@ -55,6 +57,17 @@ module Middleman
@rack_app = app
@middleman_app = options.fetch(:middleman_app)
@rewriters = options.fetch(:rewriters)
all_source_exts = @rewriters
.reduce([]) { |sum, rewriter| sum + rewriter[:source_extensions] }
.flatten
.uniq
@source_exts_regex_text = Regexp.union(all_source_exts).to_s
@all_asset_exts = @rewriters
.reduce([]) { |sum, rewriter| sum + rewriter[:url_extensions] }
.flatten
.uniq
end
def call(env)
@ -63,27 +76,16 @@ module Middleman
# Allow configuration or upstream request to skip all rewriting
return [status, headers, response] if env['bypass_inline_url_rewriter'] == 'true'
all_source_exts = @rewriters
.reduce([]) { |sum, rewriter| sum + rewriter[:source_extensions] }
.flatten
.uniq
source_exts_regex_text = Regexp.union(all_source_exts).to_s
all_asset_exts = @rewriters
.reduce([]) { |sum, rewriter| sum + rewriter[:url_extensions] }
.flatten
.uniq
path = ::Middleman::Util.full_path(env['PATH_INFO'], @middleman_app)
return [status, headers, response] unless path =~ /(^\/$)|(#{source_exts_regex_text}$)/
return [status, headers, response] unless path =~ /(^\/$)|(#{@source_exts_regex_text}$)/
return [status, headers, response] unless body = ::Middleman::Util.extract_response_text(response)
dirpath = ::Pathname.new(File.dirname(path))
rewritten = ::Middleman::Util.instrument 'inline_url_rewriter', path: path do
::Middleman::Util.rewrite_paths(body, path, all_asset_exts, @middleman_app) do |asset_path|
uri = ::Addressable::URI.parse(asset_path)
::Middleman::Util.rewrite_paths(body, path, @all_asset_exts, @middleman_app) do |asset_path|
uri = ::Middleman::Util.parse_uri(asset_path)
relative_path = uri.host.nil?
@ -106,7 +108,7 @@ module Middleman
next unless source_exts.include?(::File.extname(path))
ignore = rewriter.fetch(:ignore)
next if ignore.any? { |r| should_ignore?(r, full_asset_path) }
next if ignore.any? { |r| ::Middleman::Util.should_ignore?(r, full_asset_path) }
rewrite_ignore = Array(rewriter[:rewrite_ignore] || [])
next if rewrite_ignore.any? { |i| ::Middleman::Util.path_match(i, path) }
@ -127,23 +129,6 @@ module Middleman
headers
).finish
end
Contract IGNORE_DESCRIPTOR, String => Bool
def should_ignore?(validator, value)
if validator.is_a? Regexp
# Treat as Regexp
!!(value =~ validator)
elsif validator.respond_to? :call
# Treat as proc
validator.call(value)
elsif validator.is_a? String
# Treat as glob
File.fnmatch(value, validator)
else
# If some unknown thing, don't ignore
false
end
end
end
end
end

View File

@ -1,4 +1,5 @@
require 'forwardable'
require 'memoist'
require 'active_support/core_ext/class/attribute'
require 'middleman-core/configuration'
require 'middleman-core/contracts'
@ -66,6 +67,8 @@ module Middleman
# @see http://middlemanapp.com/advanced/custom/ Middleman Custom Extensions Documentation
class Extension
extend Forwardable
extend Memoist
include Contracts
def_delegator :@app, :logger
@ -510,7 +513,7 @@ module Middleman
self.class.exposed_to_config.each do |k, v|
::Middleman::CoreExtensions::Collections::StepContext.add_to_context(k) do |*args, &b|
r = context.method(:"__original_#{v}").call(*args, &b)
self.descriptors << r if r.respond_to?(:execute_descriptor)
descriptors << r if r.respond_to?(:execute_descriptor)
end
end
end

View File

@ -6,6 +6,7 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
option :exts, nil, 'List of extensions that get asset hashes appended to them.'
option :ignore, [], 'Regexes of filenames to skip adding asset hashes to'
option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites'
option :prefix, '', 'Prefix for hash'
def initialize(app, options_hash={}, &block)
super
@ -32,7 +33,7 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
Contract String, Or[String, Pathname], Any => Maybe[String]
def rewrite_url(asset_path, dirpath, _request_path)
uri = ::Addressable::URI.parse(asset_path)
uri = ::Middleman::Util.parse_uri(asset_path)
relative_path = !uri.path.start_with?('/')
full_asset_path = if relative_path
@ -92,7 +93,7 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
::Digest::SHA1.hexdigest(response.body)[0..7]
end
resource.destination_path = resource.destination_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
resource.destination_path = resource.destination_path.sub(/\.(\w+)$/) { |ext| "-#{options.prefix}#{digest}#{ext}" }
resource
end

View File

@ -20,7 +20,7 @@ class Middleman::Extensions::AssetHost < ::Middleman::Extension
Contract String, Or[String, Pathname], Any => String
def rewrite_url(asset_path, dirpath, _request_path)
uri = ::Addressable::URI.parse(asset_path)
uri = ::Middleman::Util.parse_uri(asset_path)
relative_path = uri.path[0..0] != '/'
full_asset_path = if relative_path
@ -37,4 +37,5 @@ class Middleman::Extensions::AssetHost < ::Middleman::Extension
File.join(asset_prefix, full_asset_path)
end
memoize :rewrite_url
end

View File

@ -1,3 +1,5 @@
require 'active_support/core_ext/object/try'
require 'memoist'
require 'middleman-core/contracts'
# Minify CSS Extension
@ -30,6 +32,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
# Rack middleware to look for CSS and compress it
class Rack
extend Memoist
include Contracts
INLINE_CSS_REGEX = /(<style[^>]*>\s*(?:\/\*<!\[CDATA\[\*\/\n)?)(.*?)((?:(?:\n\s*)?\/\*\]\]>\*\/)?\s*<\/style>)/m
@ -69,7 +72,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
end
if minified
headers['Content-Length'] = ::Rack::Utils.bytesize(minified).to_s
headers['Content-Length'] = minified.bytesize.to_s
response = [minified]
end
@ -82,8 +85,9 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
# @param [String] path
# @return [Boolean]
def ignore?(path)
@ignore.any? { |ignore| Middleman::Util.path_match(ignore, path) }
@ignore.any? { |ignore| ::Middleman::Util.path_match(ignore, path) }
end
memoize :ignore?
# Whether this type of content can be minified
# @param [String, nil] content_type
@ -91,6 +95,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
def minifiable?(content_type)
@content_types.include?(content_type)
end
memoize :minifiable?
# Whether this type of content contains inline content that can be minified
# @param [String, nil] content_type
@ -98,6 +103,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
def minifiable_inline?(content_type)
@inline_content_types.include?(content_type)
end
memoize :minifiable_inline?
# Minify the content
# @param [String] content
@ -105,6 +111,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
def minify(content)
@compressor.compress(content)
end
memoize :minify
# Detect and minify inline content
# @param [String] content
@ -114,5 +121,6 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
$1 + minify($2) + $3
end
end
memoize :minify_inline
end
end

View File

@ -1,4 +1,6 @@
require 'active_support/core_ext/object/try'
require 'middleman-core/contracts'
require 'memoist'
# Minify Javascript Extension
class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
@ -22,6 +24,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
# Rack middleware to look for JS and compress it
class Rack
extend Memoist
include Contracts
INLINE_JS_REGEX = /(<script[^>]*>\s*(?:\/\/(?:(?:<!--)|(?:<!\[CDATA\[))\n)?)(.*?)((?:(?:\n\s*)?\/\/(?:(?:-->)|(?:\]\]>)))?\s*<\/script>)/m
@ -61,7 +64,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
end
if minified
headers['Content-Length'] = ::Rack::Utils.bytesize(minified).to_s
headers['Content-Length'] = minified.bytesize.to_s
response = [minified]
end
@ -76,6 +79,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
def ignore?(path)
@ignore.any? { |ignore| Middleman::Util.path_match(ignore, path) }
end
memoize :ignore?
# Whether this type of content can be minified
# @param [String, nil] content_type
@ -83,6 +87,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
def minifiable?(content_type)
@content_types.include?(content_type)
end
memoize :minifiable?
# Whether this type of content contains inline content that can be minified
# @param [String, nil] content_type
@ -90,6 +95,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
def minifiable_inline?(content_type)
@inline_content_types.include?(content_type)
end
memoize :minifiable_inline?
# Minify the content
# @param [String] content
@ -100,6 +106,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
warn "WARNING: Couldn't compress JavaScript in #{@path}: #{e.message}"
content
end
memoize :minify
# Detect and minify inline content
# @param [String] content
@ -119,5 +126,6 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
end
end
end
memoize :minify_inline
end
end

View File

@ -2,15 +2,18 @@ require 'addressable/uri'
# Relative Assets extension
class Middleman::Extensions::RelativeAssets < ::Middleman::Extension
option :exts, nil, 'List of extensions that get cache busters strings appended to them.'
option :exts, nil, 'List of extensions that get converted to relative paths.'
option :sources, %w(.css .htm .html .xhtml), 'List of extensions that are searched for relative assets.'
option :ignore, [], 'Regexes of filenames to skip adding query strings to'
option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites'
option :ignore, [], 'Regexes of filenames to skip converting to relative paths.'
option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites.'
option :helpers_only, false, 'Allow only Ruby helpers to change paths.'
def initialize(app, options_hash={}, &block)
super
app.rewrite_inline_urls id: :asset_hash,
return if options[:helpers_only]
app.rewrite_inline_urls id: :relative_assets,
url_extensions: options.exts || app.config[:asset_extensions],
source_extensions: options.sources,
ignore: options.ignore,
@ -18,22 +21,43 @@ 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)
def mark_as_relative(file_path, opts, current_resource)
result = opts.dup
super(path, prefix, options)
valid_exts = options.sources
return result unless current_resource
return result unless valid_exts.include?(current_resource.ext)
rewrite_ignores = Array(options.rewrite_ignore || [])
path = current_resource.destination_path
return result if rewrite_ignores.any? do |i|
::Middleman::Util.path_match(i, path) || ::Middleman::Util.path_match(i, "/#{path}")
end
return result if Array(options.ignore || []).any? do |r|
::Middleman::Util.should_ignore?(r, file_path)
end
result[:relative] = true unless result.key?(:relative)
result
end
helpers do
def asset_url(path, prefix='', options={})
super(path, prefix, app.extensions[:relative_assets].mark_as_relative(super, options, current_resource))
end
def asset_path(kind, source, options={})
super(kind, source, app.extensions[:relative_assets].mark_as_relative(super, options, current_resource))
end
end
Contract String, Or[String, Pathname], Any => Maybe[String]
def rewrite_url(asset_path, dirpath, request_path)
uri = ::Addressable::URI.parse(asset_path)
uri = ::Middleman::Util.parse_uri(asset_path)
return if uri.path[0..0] != '/'
@ -50,4 +74,5 @@ class Middleman::Extensions::RelativeAssets < ::Middleman::Extension
result
end
memoize :rewrite_url
end

View File

@ -3,8 +3,8 @@ require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/module/delegation'
require 'middleman-core/contracts'
::Tilt.mappings.delete('html') # WTF, Tilt?
::Tilt.mappings.delete('csv')
::Tilt.default_mapping.lazy_map.delete('html')
::Tilt.default_mapping.lazy_map.delete('csv')
module Middleman
class FileRenderer
@ -59,7 +59,7 @@ module Middleman
# Overwrite with frontmatter options
options = options.deep_merge(options[:renderer_options]) if options[:renderer_options]
template_class = ::Tilt[path]
template_class = ::Middleman::Util.tilt_class(path)
# Allow hooks to manipulate the template before render
body = @app.callbacks_for(:before_render).reduce(body) do |sum, callback|
@ -99,12 +99,12 @@ module Middleman
def template_data_for_file
file = @app.files.find(:source, @path)
if @app.extensions[:front_matter] || (file && !file[:types].include?(:no_frontmatter))
if @app.extensions[:front_matter] && (file && !file[:types].include?(:no_frontmatter))
result = @app.extensions[:front_matter].template_data_for_file(@path)
return result unless result.nil?
end
file ? file.read : File.read(@path)
file ? file.read : ::File.read(@path)
end
protected
@ -122,9 +122,9 @@ module Middleman
# Find all the engines which handle this extension in tilt. Look for
# config variables of that name and merge it
extension_class = ::Tilt[ext]
::Tilt.mappings.each do |mapping_ext, engines|
next unless engines.include? extension_class
extension_class = ::Middleman::Util.tilt_class(ext)
::Tilt.default_mapping.extensions_for(extension_class).each do |mapping_ext|
engine_options = @app.config[mapping_ext.to_sym] || {}
options.merge!(engine_options)
end

View File

@ -143,7 +143,7 @@ module Middleman
app = ::Middleman::Application.new do
config[:cli_options] = cli_options.each_with_object({}) do |(k, v), sum|
sum[k] = v unless v == :undefined
sum[k] = v
end
ready do
@ -160,6 +160,9 @@ module Middleman
path: root,
only: match_against
# Hack around bower_components in root.
watcher.listener.ignore(/^bower_components/)
# Hack around node_modules in root.
watcher.listener.ignore(/^node_modules/)
@ -204,7 +207,7 @@ module Middleman
end
def possible_from_cli(key, config)
if @cli_options[key] && @cli_options[key] != :undefined
if @cli_options[key]
@cli_options[key]
else
config[key]

View File

@ -133,9 +133,15 @@ module Middleman
# Immediately send static file
def send_file(resource, env)
file = ::Rack::File.new nil
file.path = resource.file_descriptor[:full_path]
response = file.serving(env)
file = ::Rack::File.new nil
path = resource.file_descriptor[:full_path]
if !file.respond_to?(:path=)
request = ::Rack::Request.new(env)
response = file.serving(request, path)
else
file.path = path
response = file.serving(env)
end
status = response[0]
response[1]['Content-Encoding'] = 'gzip' if %w(.svgz .gz).include?(resource.ext)
# Do not set Content-Type if status is 1xx, 204, 205 or 304, otherwise

View File

@ -3,7 +3,7 @@ require 'active_support/core_ext/module/attribute_accessors'
module Middleman
module Renderers
class RedcarpetTemplate < ::Tilt::RedcarpetTemplate::Redcarpet2
class RedcarpetTemplate < ::Tilt::RedcarpetTemplate
# because tilt has decided to convert these
# in the wrong direction
ALIASES = {

View File

@ -105,10 +105,8 @@ module Middleman
end
end
SASS_MODULE = if defined?(::SassC)
::SassC
else
::Sass
if defined?(::SassC)
::SassC::Script::Functions.send :include, ::Middleman::Sass::Functions
elsif defined?(::Sass)
::Sass::Script::Functions.send :include, ::Middleman::Sass::Functions
end
SASS_MODULE::Script::Functions.send :include, ::Middleman::Sass::Functions

View File

@ -12,13 +12,9 @@ class ::Slim::Template
def initialize(file, line, opts, &block)
if opts.key?(:context)
context_hack = {
context: opts[:context]
}
::Slim::Embedded::SassEngine.disable_option_validator!
%w(sass scss markdown).each do |engine|
::Slim::Embedded.options[engine.to_sym] = context_hack
(::Slim::Embedded.options[engine.to_sym] ||= {})[:context] = opts[:context]
end
end

View File

@ -14,16 +14,31 @@ module Middleman
Contract Or[String, Regexp, Proc] => RespondTo[:execute_descriptor]
def ignore(path=nil, &block)
@app.sitemap.invalidate_resources_not_ignored_cache!
IgnoreDescriptor.new(path, block)
if path.is_a? Regexp
RegexpIgnoreDescriptor.new(path)
elsif path.is_a? String
path_clean = ::Middleman::Util.normalize_path(path)
if path_clean.include?('*') # It's a glob
GlobIgnoreDescriptor.new(path_clean)
else
StringIgnoreDescriptor.new(path_clean)
end
elsif block
BlockIgnoreDescriptor.new(nil, block)
else
IgnoreDescriptor.new(path, block)
end
end
IgnoreDescriptor = Struct.new(:path, :block) do
def execute_descriptor(_app, resources)
resources.map do |r|
# Ignore based on the source path (without template extensions)
if ignored?(r.path)
if ignored?(r.normalized_path)
r.ignore!
elsif !r.is_a?(ProxyResource) && r.file_descriptor && ignored?(r.file_descriptor[:relative_path].to_s)
elsif !r.is_a?(ProxyResource) && r.file_descriptor && ignored?(r.file_descriptor.normalized_relative_path)
# This allows files to be ignored by their source file name (with template extensions)
r.ignore!
end
@ -33,27 +48,38 @@ module Middleman
end
def ignored?(match_path)
match_path = ::Middleman::Util.normalize_path(match_path)
raise NotImplementedError
end
end
if path.is_a? Regexp
match_path =~ path
elsif path.is_a? String
path_clean = ::Middleman::Util.normalize_path(path)
class RegexpIgnoreDescriptor < IgnoreDescriptor
def ignored?(match_path)
match_path =~ path
end
end
if path_clean.include?('*') # It's a glob
if defined?(::File::FNM_EXTGLOB)
::File.fnmatch(path_clean, match_path, ::File::FNM_EXTGLOB)
else
::File.fnmatch(path_clean, match_path)
end
else
match_path == path_clean
end
elsif block
block.call(match_path)
class GlobIgnoreDescriptor < IgnoreDescriptor
def ignored?(match_path)
if defined?(::File::FNM_EXTGLOB)
::File.fnmatch(path, match_path, ::File::FNM_EXTGLOB)
else
::File.fnmatch(path, match_path)
end
end
end
class StringIgnoreDescriptor < IgnoreDescriptor
def ignored?(match_path)
match_path == path
end
end
class BlockIgnoreDescriptor < IgnoreDescriptor
def ignored?(match_path)
block.call(match_path)
end
end
end
end
end

View File

@ -47,7 +47,7 @@ module Middleman
)
if should_ignore
d = ::Middleman::Sitemap::Extensions::Ignores::IgnoreDescriptor.new(target)
d = ::Middleman::Sitemap::Extensions::Ignores::StringIgnoreDescriptor.new(target)
d.execute_descriptor(app, resources)
end

View File

@ -53,8 +53,7 @@ module Middleman
def render(*)
url = ::Middleman::Util.url_for(@store.app, @request_path,
relative: false,
find_resource: true
)
find_resource: true)
if output
output.call(path, url)

View File

@ -75,19 +75,19 @@ module Middleman
Contract Bool
def template?
return false if file_descriptor.nil?
!::Tilt[file_descriptor[:full_path].to_s].nil?
!::Middleman::Util.tilt_class(file_descriptor[:full_path].to_s).nil?
end
# Backwards compatible method for turning descriptor into a string.
# @return [String]
Contract String
Contract Maybe[String]
def source_file
file_descriptor && file_descriptor[:full_path].to_s
end
Contract Or[Symbol, String]
Contract Or[Symbol, String, Fixnum]
def page_id
metadata[:page][:id] || destination_path
metadata[:page][:id] || make_implicit_page_id(destination_path)
end
# Merge in new metadata specific to this resource.
@ -197,10 +197,41 @@ module Middleman
options[:content_type] || ::Rack::Mime.mime_type(ext, nil)
end
# The normalized source path of this resource (relative to the source directory,
# without template extensions)
# @return [String]
def normalized_path
@normalized_path ||= ::Middleman::Util.normalize_path @path
end
def to_s
"#<#{self.class} path=#{@path}>"
end
alias inspect to_s # Ruby 2.0 calls inspect for NoMethodError instead of to_s
protected
# Makes a page id based on path (when not otherwise given)
#
# Removes .html extension and potential leading slashes or dots
# eg. "foo/bar/baz.foo.html" => "foo/bar/baz.foo"
Contract String => String
def make_implicit_page_id(path)
@id ||= begin
if prok = @app.config[:page_id_generator]
return prok.call(path)
end
basename = if ext == ".html"
File.basename(path, ext)
else
File.basename(path)
end
# Remove leading dot or slash if present
File.join(File.dirname(path), basename).gsub(/^\.?\//, '')
end
end
end
class StringResource < Resource

View File

@ -102,7 +102,7 @@ module Middleman
# @param [Numeric] priority Sets the order of this resource list manipulator relative to the rest. By default this is 50, and manipulators run in the order they are registered, but if a priority is provided then this will run ahead of or behind other manipulators.
# @param [Symbol] custom_name The method name to execute.
# @return [void]
Contract Symbol, RespondTo[:manipulate_resource_list], Maybe[Num], Maybe[Symbol] => Any
Contract Symbol, RespondTo[:manipulate_resource_list], Maybe[Num, Bool], Maybe[Symbol] => Any
def register_resource_list_manipulator(name, manipulator, priority=50, custom_name=nil)
# The third argument used to be a boolean - handle those who still pass one
priority = 50 unless priority.is_a? Numeric

View File

@ -8,6 +8,10 @@ module Middleman
::Middleman::Sources.file_cache[full_path] ||= {}
::Middleman::Sources.file_cache[full_path][version] ||= ::File.read(full_path)
end
def normalized_relative_path
@normalized_relative_path ||= ::Middleman::Util.normalize_path relative_path.to_s
end
end
# Sources handle multiple on-disk collections of files which make up
@ -52,15 +56,13 @@ module Middleman
# @param [Hash] options Global options.
# @param [Array] watchers Default watchers.
Contract IsA['Middleman::Application'], Maybe[Hash], Maybe[Array] => Any
def initialize(app, options={}, watchers=[])
def initialize(app, _options={}, watchers=[])
@app = app
@watchers = watchers
@sorted_watchers = @watchers.dup.freeze
::Middleman::Sources.file_cache = {}
@options = options
# Set of procs wanting to be notified of changes
@on_change_callbacks = ::Hamster::Vector.empty
@ -168,7 +170,7 @@ module Middleman
# @return [Middleman::Sources]
Contract Symbol => ::Middleman::Sources
def by_type(type)
self.class.new @app, @options, watchers.select { |d| d.type == type }
self.class.new @app, nil, watchers.select { |d| d.type == type }
end
# Get all files for this collection of watchers.

View File

@ -75,9 +75,10 @@ module Middleman
@ignored = options.fetch(:ignored, proc { false })
@only = Array(options.fetch(:only, []))
@disable_watcher = app.build? || @parent.options.fetch(:disable_watcher, false)
@force_polling = @parent.options.fetch(:force_polling, false)
@latency = @parent.options.fetch(:latency, nil)
@disable_watcher = app.build?
@force_polling = false
@latency = nil
@wait_for_delay = nil
@listener = nil
@ -95,13 +96,20 @@ module Middleman
def update_path(directory)
@directory = Pathname(File.expand_path(directory, app.root))
stop_listener! if @listener
update([], @files.values.map { |source_file| source_file[:full_path] })
without_listener_running do
update([], @files.values.map { |source_file| source_file[:full_path] })
end
poll_once!
end
listen! unless @disable_watcher
def update_config(options={})
without_listener_running do
@disable_watcher = options.fetch(:disable_watcher, false)
@force_polling = options.fetch(:force_polling, false)
@latency = options.fetch(:latency, nil)
@wait_for_delay = options.fetch(:wait_for_delay, nil)
end
end
# Stop watching.
@ -160,10 +168,10 @@ module Middleman
config = {
force_polling: @force_polling,
wait_for_delay: 0.5
}
config[:latency] = @latency if @latency
config[:wait_for_delay] = @wait_for_delay.try(:to_f) || 0.5
config[:latency] = @latency.to_f if @latency
@listener = ::Listen.to(@directory.to_s, config, &method(:on_listener_change))
@ -199,7 +207,7 @@ module Middleman
Contract ArrayOf[Pathname]
def poll_once!
updated = ::Middleman::Util.all_files_under(@directory.to_s, &method(:should_not_recurse?))
removed = @files.keys.reject { |p| updated.include?(p) }
removed = @files.keys - updated
result = update(updated, removed)
@ -298,9 +306,15 @@ module Middleman
relative_path = path.relative_path_from(directory)
relative_path = File.join(destination_dir, relative_path) if destination_dir
types << :no_frontmatter if partial?(relative_path.to_s)
::Middleman::SourceFile.new(Pathname(relative_path), path, directory, types, 0)
end
def partial?(relative_path)
relative_path.split(::File::SEPARATOR).any? { |p| p.start_with?('_') }
end
Contract IsA['Middleman::SourceFile'] => Any
def record_file_change(f)
if @files[f[:full_path]]
@ -337,5 +351,20 @@ module Middleman
@only.any? { |reg| file[:relative_path].to_s =~ reg }
end
end
private
def without_listener_running
listener_running = @listener && @listener.processing?
stop_listener! if listener_running
yield
if listener_running
poll_once!
listen!
end
end
end
end

View File

@ -23,7 +23,7 @@ module Middleman
attr_accessor :current_engine
# Shorthand references to global values on the app instance.
def_delegators :@app, :config, :logger, :sitemap, :server?, :build?, :environment?, :environment, :data, :extensions, :root
def_delegators :@app, :config, :logger, :sitemap, :server?, :build?, :environment?, :environment, :data, :extensions, :root, :development?, :production?
# Initialize a context with the current app and predefined locals and options hashes.
#
@ -84,6 +84,7 @@ module Middleman
# Reset stored buffer, regardless of success
restore_buffer(buf_was)
end
# Render the layout, with the contents of the block inside.
concat_safe_content render_file(layout_file, @locs, @opts) { content }
ensure
@ -126,30 +127,34 @@ module Middleman
# @return [String]
Contract String, Maybe[Bool] => Maybe[IsA['Middleman::SourceFile']]
def locate_partial(partial_path, try_static=true)
return unless resource = sitemap.find_resource_by_destination_path(current_path)
# Look for partials relative to the current path
current_dir = resource.file_descriptor[:relative_path].dirname
non_root = partial_path.to_s.sub(/^\//, '')
relative_dir = current_dir + Pathname(non_root)
non_root_no_underscore = non_root.sub(/^_/, '').sub(/\/_/, '/')
relative_dir_no_underscore = current_dir + Pathname(non_root_no_underscore)
partial_file = nil
lookup_stack = []
non_root = partial_path.to_s.sub(/^\//, '')
non_root_no_underscore = non_root.sub(/^_/, '').sub(/\/_/, '/')
[
[relative_dir.to_s, { preferred_engine: resource.file_descriptor[:relative_path].extname[1..-1].to_sym }],
[non_root],
[non_root, { try_static: try_static }],
[relative_dir_no_underscore.to_s, { try_static: try_static }],
[non_root_no_underscore, { try_static: try_static }]
].each do |args|
if resource = current_resource
current_dir = resource.file_descriptor[:relative_path].dirname
relative_dir = current_dir + Pathname(non_root)
relative_dir_no_underscore = current_dir + Pathname(non_root_no_underscore)
end
lookup_stack.push [relative_dir.to_s,
{ preferred_engine: resource.file_descriptor[:relative_path]
.extname[1..-1].to_sym }] if relative_dir
lookup_stack.push [non_root]
lookup_stack.push [non_root,
{ try_static: try_static }]
lookup_stack.push [relative_dir_no_underscore.to_s,
{ try_static: try_static }] if relative_dir_no_underscore
lookup_stack.push [non_root_no_underscore,
{ try_static: try_static }]
lookup_stack.each do |args|
partial_file = ::Middleman::TemplateRenderer.resolve_template(@app, *args)
break if partial_file
end
partial_file || nil
partial_file
end
def current_path
@ -185,7 +190,7 @@ module Middleman
# handles cases like `style.css.sass.erb`
content = nil
while ::Tilt[path]
while ::Middleman::Util.tilt_class(path)
begin
opts[:template_body] = content if content

View File

@ -61,12 +61,10 @@ module Middleman
# If we're specifically looking for a preferred engine
if options.key?(:preferred_engine)
extension_class = ::Tilt[options[:preferred_engine]]
extension_class = ::Middleman::Util.tilt_class(options[:preferred_engine])
# Get a list of extensions for a preferred engine
preferred_engines += ::Tilt.mappings.select do |_, engines|
engines.include? extension_class
end.keys
preferred_engines += ::Tilt.default_mapping.extensions_for(extension_class)
end
preferred_engines << '*'
@ -89,7 +87,7 @@ module Middleman
app.files.find(:source, path_with_ext, globbing)
end
found_template = file if file && (preferred_engine.nil? || ::Tilt[file[:full_path]])
found_template = file if file && (preferred_engine.nil? || ::Middleman::Util.tilt_class(file[:full_path].to_s))
break if found_template
end
@ -133,20 +131,33 @@ module Middleman
# Add extension helpers to context.
@app.extensions.add_exposed_to_context(context)
locals.each do |k, _|
next unless context.respond_to?(k) && ![:current_path, :paginate, :page_articles, :blog_controller, :lang, :locale].include?(k.to_sym)
msg = "Template local `#{k}` tried to overwrite an existing context value. Please rename the key when passing to `locals`"
if @app.build?
throw msg
else
@app.logger.error(msg)
end
end
content = ::Middleman::Util.instrument 'builder.output.resource.render-template', path: File.basename(path) do
_render_with_all_renderers(path, locs, context, opts, &block)
_render_with_all_renderers(path, locals, context, options, &block)
end
# If we need a layout and have a layout, use it
layout_file = fetch_layout(engine, options)
if layout_file
content = ::Middleman::Util.instrument 'builder.output.resource.render-layout', path: File.basename(layout_file[:relative_path].to_s) do
if layout_file = fetch_layout(engine, options)
layout_renderer = ::Middleman::FileRenderer.new(@app, layout_file[:relative_path].to_s)
content = if layout_file = fetch_layout(engine, options)
layout_renderer = ::Middleman::FileRenderer.new(@app, layout_file[:relative_path].to_s)
::Middleman::Util.instrument 'builder.output.resource.render-layout', path: File.basename(layout_file[:relative_path].to_s) do
layout_renderer.render(locals, options, context) { content }
else
content
end
else
content
end
end
@ -165,7 +176,7 @@ module Middleman
# handles cases like `style.css.sass.erb`
content = nil
while ::Tilt[path]
while ::Middleman::Util.tilt_class(path)
begin
opts[:template_body] = content if content

View File

@ -47,14 +47,13 @@ module Middleman
# @return [Boolean]
Contract String => Bool
def nonbinary_mime?(mime)
case
when mime.start_with?('text/')
if mime.start_with?('text/')
true
when mime.include?('xml') && !mime.include?('officedocument')
elsif mime.include?('xml') && !mime.include?('officedocument')
true
when mime.include?('json')
elsif mime.include?('json')
true
when mime.include?('javascript')
elsif mime.include?('javascript')
true
else
false

View File

@ -3,6 +3,7 @@ require 'json'
require 'pathname'
require 'backports/2.1.0/array/to_h'
require 'hashie'
require 'memoist'
require 'middleman-core/util/binary'
require 'middleman-core/contracts'
@ -36,6 +37,7 @@ module Middleman
end
module Data
extend Memoist
include Contracts
module_function
@ -55,20 +57,7 @@ module Middleman
return [{}, nil]
end
start_delims, stop_delims = frontmatter_delims
.values
.flatten(1)
.transpose
.map(&::Regexp.method(:union))
match = /
\A(?:[^\r\n]*coding:[^\r\n]*\r?\n)?
(?<start>#{start_delims})[ ]*\r?\n
(?<frontmatter>.*?)[ ]*\r?\n?
^(?<stop>#{stop_delims})[ ]*\r?\n?
\r?\n?
(?<additional_content>.*)
/mx.match(content) || {}
match = build_regex(frontmatter_delims).match(content) || {}
unless match[:frontmatter]
case known_type
@ -98,27 +87,53 @@ module Middleman
end
end
def build_regex(frontmatter_delims)
start_delims, stop_delims = frontmatter_delims
.values
.flatten(1)
.transpose
.map(&::Regexp.method(:union))
/
\A(?:[^\r\n]*coding:[^\r\n]*\r?\n)?
(?<start>#{start_delims})[ ]*\r?\n
(?<frontmatter>.*?)[ ]*\r?\n?
^(?<stop>#{stop_delims})[ ]*\r?\n?
\r?\n?
(?<additional_content>.*)
/mx
end
memoize :build_regex
# Parse YAML frontmatter out of a string
# @param [String] content
# @return [Hash]
Contract String, Pathname, Bool => Hash
Contract String, Pathname => Hash
def parse_yaml(content, full_path)
symbolize_recursive(::YAML.load(content) || {})
c = ::Middleman::Util.instrument 'parse.yaml' do
::YAML.load(content)
end
c ? symbolize_recursive(c) : {}
rescue StandardError, ::Psych::SyntaxError => error
warn "YAML Exception parsing #{full_path}: #{error.message}"
{}
end
memoize :parse_yaml
# Parse JSON frontmatter out of a string
# @param [String] content
# @return [Hash]
Contract String, Pathname => Hash
def parse_json(content, full_path)
symbolize_recursive(::JSON.parse(content) || {})
c = ::Middleman::Util.instrument 'parse.json' do
::JSON.parse(content)
end
c ? symbolize_recursive(c) : {}
rescue StandardError => error
warn "JSON Exception parsing #{full_path}: #{error.message}"
{}
end
memoize :parse_json
def symbolize_recursive(value)
case value

View File

@ -54,16 +54,12 @@ module Middleman
result.encode('UTF-8', 'UTF-8-MAC')
end
Contract String => Bool
def tilt_recognizes?(path)
@@tilt_lookup_cache ||= {}
@@tilt_lookup_cache[path] ||= ::Tilt[path]
end
Contract String => String
def step_through_extensions(path)
while tilt_recognizes?(path)
while ::Middleman::Util.tilt_class(path)
ext = ::File.extname(path)
break if ext.empty?
yield ext if block_given?
# Strip templating extensions as long as Tilt knows them

View File

@ -1,24 +1,41 @@
# Core Pathname library used for traversal
require 'pathname'
require 'uri'
require 'addressable/uri'
require 'memoist'
require 'tilt'
require 'middleman-core/contracts'
# rubocop:disable ModuleLength
module Middleman
module Util
extend Memoist
include Contracts
module_function
Contract String => ::Addressable::URI
def parse_uri(uri)
::Addressable::URI.parse(uri)
end
memoize :parse_uri
Contract String => Any
def tilt_class(path)
::Tilt[path]
end
memoize :tilt_class
# Normalize a path to not include a leading slash
# @param [String] path
# @return [String]
Contract String => String
def normalize_path(path)
# The tr call works around a bug in Ruby's Unicode handling
# The tr call works around a bug in Ruby's Unicode handling
::URI.decode(path).sub(%r{^/}, '').tr('', '')
end
end
memoize :normalize_path
# This is a separate method from normalize_path in case we
# change how we normalize paths
@ -26,6 +43,26 @@ module Middleman
def strip_leading_slash(path)
path.sub(%r{^/}, '')
end
memoize :strip_leading_slash
IGNORE_DESCRIPTOR = Or[Regexp, RespondTo[:call], String]
Contract IGNORE_DESCRIPTOR, String => Bool
def should_ignore?(validator, value)
if validator.is_a? Regexp
# Treat as Regexp
!!(value =~ validator)
elsif validator.respond_to? :call
# Treat as proc
validator.call(value)
elsif validator.is_a? String
# Treat as glob
File.fnmatch(value, validator)
else
# If some unknown thing, don't ignore
false
end
end
memoize :should_ignore?
# Get the path of a file of a given type
#
@ -74,14 +111,16 @@ module Middleman
raise ArgumentError, '#asset_url must be run in a context with current_resource if relative: true'
end
uri = URI(path)
uri = ::Middleman::Util.parse_uri(path)
path = uri.path
# Ensure the url we pass into find_resource_by_destination_path is not a
# relative path, since it only takes absolute url paths.
dest_path = url_for(app, path, options.merge(relative: false))
result = if resource = app.sitemap.find_resource_by_destination_path(dest_path)
result = if resource = app.sitemap.find_resource_by_path(dest_path)
resource.url
elsif resource = app.sitemap.find_resource_by_destination_path(dest_path)
resource.url
else
path = ::File.join(prefix, path)
@ -92,9 +131,15 @@ module Middleman
end
end
final_result = ::URI.encode(relative_path_from_resource(options[:current_resource], result, options[:relative]))
final_result = ::Addressable::URI.encode(
relative_path_from_resource(
options[:current_resource],
result,
options[:relative]
)
)
result_uri = URI(final_result)
result_uri = ::Middleman::Util.parse_uri(final_result)
result_uri.query = uri.query
result_uri.fragment = uri.fragment
result_uri.to_s
@ -107,7 +152,8 @@ module Middleman
def url_for(app, path_or_resource, options={})
if path_or_resource.is_a?(String) || path_or_resource.is_a?(Symbol)
r = app.sitemap.find_resource_by_page_id(path_or_resource)
path_or_resource = r if r
path_or_resource = r ? r : path_or_resource.to_s
end
# Handle Resources and other things which define their own url method
@ -119,8 +165,8 @@ module Middleman
# Try to parse URL
begin
uri = URI(url)
rescue ::URI::InvalidURIError
uri = ::Middleman::Util.parse_uri(url)
rescue ::Addressable::URI::InvalidURIError
# Nothing we can do with it, it's not really a URI
return url
end
@ -163,7 +209,13 @@ module Middleman
if resource
uri.path = if this_resource
::URI.encode(relative_path_from_resource(this_resource, resource_url, effective_relative))
::Addressable::URI.encode(
relative_path_from_resource(
this_resource,
resource_url,
effective_relative
)
)
else
resource_url
end
@ -241,20 +293,20 @@ module Middleman
# @return [Boolean] Whether the path matches the matcher
Contract PATH_MATCHER, String => Bool
def path_match(matcher, path)
case
when matcher.is_a?(String)
if matcher.is_a?(String)
if matcher.include? '*'
::File.fnmatch(matcher, path)
else
path == matcher
end
when matcher.respond_to?(:match)
elsif matcher.respond_to?(:match)
!!(path =~ matcher)
when matcher.respond_to?(:call)
elsif matcher.respond_to?(:call)
matcher.call(path)
else
::File.fnmatch(matcher.to_s, path)
end
end
memoize :path_match
end
end

View File

@ -38,7 +38,7 @@ module Middleman
current_resource = app.sitemap.find_resource_by_destination_path(path)
begin
uri = ::Addressable::URI.parse(asset_path)
uri = ::Middleman::Util.parse_uri(asset_path)
if uri.relative? && uri.host.nil? && !(asset_path =~ /^[^\/].*[a-z]+\.[a-z]+\/.*/)
dest_path = ::Middleman::Util.url_for(app, asset_path, relative: false, current_resource: current_resource)

View File

@ -1,5 +1,5 @@
module Middleman
# Current Version
# @return [String]
VERSION = '4.1.7'.freeze unless const_defined?(:VERSION)
VERSION = '4.1.14'.freeze unless const_defined?(:VERSION)
end

View File

@ -16,27 +16,29 @@ 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 = '>= 2.0.0'
s.required_ruby_version = '>= 2.2.0'
# Core
s.add_dependency('bundler', ['~> 1.1'])
s.add_dependency('rack', ['>= 1.4.5', '< 2.0'])
s.add_dependency('tilt', ['~> 1.4.1'])
s.add_dependency('rack', ['>= 1.4.5', '< 3'])
s.add_dependency('tilt', ['~> 2.0'])
s.add_dependency('erubis')
s.add_dependency('fast_blank')
s.add_dependency('parallel')
s.add_dependency('servolux')
s.add_dependency('dotenv')
# Helpers
s.add_dependency('activesupport', ['~> 4.2'])
s.add_dependency('activesupport', ['>= 4.2', '< 5.1'])
s.add_dependency('padrino-helpers', ['~> 0.13.0'])
s.add_dependency("addressable", ["~> 2.3"])
s.add_dependency('memoist', ['~> 0.14'])
# Watcher
s.add_dependency('listen', ['~> 3.0'])
s.add_dependency('listen', ['~> 3.0.0'])
# Tests
s.add_dependency("capybara", ["~> 2.5.0"])
s.add_development_dependency("capybara", ["~> 2.5.0"])
# i18n
s.add_dependency('i18n', ['~> 0.7.0'])

View File

@ -162,6 +162,13 @@ describe Middleman::Util do
expect( Middleman::Util.asset_url( @mm, '/how/about/that.html' ) ).to eq '/how/about/that/'
end
it "returns a resource url when asset_hash is on" do
Given.fixture 'asset-hash-app'
@mm = Middleman::Application.new
expect( Middleman::Util.asset_url( @mm, '100px.png', 'images') ).to match %r|/images/100px-[a-f0-9]+.png|
end
end
describe "::find_related_files" do
@ -204,4 +211,20 @@ describe Middleman::Util do
expect(related).to include File.expand_path("source/stylesheets/include2.css.scss")
end
end
describe "::step_through_extensions" do
it "returns the base name after templating engine extensions are removed" do
result = Middleman::Util.step_through_extensions('my_file.html.haml.erb')
expect(result).to eq 'my_file.html'
end
it "does not loop infinitely when file name is a possible templating engine" do
expect do
Timeout::timeout(3.0) do
result = Middleman::Util.step_through_extensions("markdown.scss")
expect(result).to eq "markdown"
end
end.not_to raise_error
end
end
end

View File

@ -16,7 +16,7 @@ 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 = '>= 2.0.0'
s.required_ruby_version = '>= 2.2.0'
s.add_dependency('middleman-core', Middleman::VERSION)
s.add_dependency('middleman-cli', Middleman::VERSION)