Merge remote-tracking branch 'origin/3.0-stable'

Conflicts:
	middleman-core/lib/middleman-core/core_extensions/data.rb
	middleman-core/lib/middleman-core/core_extensions/front_matter.rb
	middleman-core/lib/middleman-core/util.rb
	middleman-core/middleman-core-x86-mingw32.gemspec
	middleman-more/lib/middleman-more/core_extensions/default_helpers.rb
	middleman-more/lib/middleman-more/core_extensions/i18n.rb
This commit is contained in:
Ben Hollis 2013-02-10 15:41:36 -08:00
commit 52c4677391
28 changed files with 440 additions and 115 deletions

View file

@ -48,11 +48,7 @@ A full set of in-depth instructional guides are available on the official websit
http://middlemanapp.com/ http://middlemanapp.com/
The community maintains its own collection of tips and tricks in the GitHub wiki: Additionally, up-to-date generated code documentation is available on RubyDoc:
https://github.com/middleman/middleman/wiki
Finally, up-to-date generated code documentation is available on RubyDoc:
http://rubydoc.info/github/middleman/middleman http://rubydoc.info/github/middleman/middleman
# Community # Community

View file

@ -87,6 +87,13 @@ task :test do
end end
end end
desc "Run specs for all middleman gems"
task :spec do
GEM_PATHS.each do |g|
sh "cd #{File.join(ROOT, g)} && #{Gem.ruby} -S rake spec"
end
end
# desc "Rune cane for all middleman gems" # desc "Rune cane for all middleman gems"
# task :cane do # task :cane do
# GEM_PATHS.each do |g| # GEM_PATHS.each do |g|

View file

@ -233,7 +233,7 @@ module Middleman::Cli
paths = ::Middleman::Util.all_files_under(@destination) paths = ::Middleman::Util.all_files_under(@destination)
@cleaning_queue += paths.select do |path| @cleaning_queue += paths.select do |path|
!path.to_s.match(/\/\./) || path.to_s.match(/\.htaccess|\.htpasswd/) path.to_s !~ /\/\./ || path.to_s =~ /\.(htaccess|htpasswd)/
end end
end end

View file

@ -24,12 +24,12 @@ module Middleman
# Setup data files before anything else so they are available when # Setup data files before anything else so they are available when
# parsing config.rb # parsing config.rb
def initialize def initialize
files.changed DataStore.matcher do |file| self.files.changed DataStore.matcher do |file|
data.touch_file(file) if file.match(%r{^#{config[:data_dir]}\/}) self.data.touch_file(file) if file.start_with?("#{config[data_dir]}/")
end end
files.deleted DataStore.matcher do |file| self.files.deleted DataStore.matcher do |file|
data.remove_file(file) if file.match(%r{^#{config[data_dir]}\/}) self.data.remove_file(file) if file.start_with?("#{config[data_dir]}/")
end end
super super

View file

@ -145,7 +145,8 @@ module Middleman
# @param [Pathname] path # @param [Pathname] path
# @return [Boolean] # @return [Boolean]
def ignored?(path) def ignored?(path)
IGNORE_LIST.any? { |r| path.to_s.match(r) } path = path.to_s
IGNORE_LIST.any? { |r| path =~ r }
end end
# Notify callbacks for a file given an array of callbacks # Notify callbacks for a file given an array of callbacks

View file

@ -1,4 +1,5 @@
require "active_support/core_ext/hash/keys" require "active_support/core_ext/hash/keys"
require 'pathname'
# Extensions namespace # Extensions namespace
module Middleman::CoreExtensions module Middleman::CoreExtensions
@ -130,7 +131,12 @@ module Middleman::CoreExtensions
# @param [String] path # @param [String] path
# @return [Array<Thor::CoreExt::HashWithIndifferentAccess, String>] # @return [Array<Thor::CoreExt::HashWithIndifferentAccess, String>]
def frontmatter_and_content(path) def frontmatter_and_content(path)
full_path = File.expand_path(File.join(@app.source_dir, path)) full_path = if Pathname(path).relative?
File.join(@app.source_dir, path)
else
path
end
data = {} data = {}
content = nil content = nil
@ -158,7 +164,7 @@ module Middleman::CoreExtensions
end end
def normalize_path(path) def normalize_path(path)
path.sub(@app.source_dir, "").sub(/^\//, "") path.sub(%r{^#{@app.source_dir}\/}, "")
end end
# Update the main sitemap resource list # Update the main sitemap resource list

View file

@ -246,15 +246,26 @@ module Middleman
options = opts.merge(options_for_ext(extension)) options = opts.merge(options_for_ext(extension))
options[:outvar] ||= '@_out_buf' options[:outvar] ||= '@_out_buf'
template_class = Tilt[path]
# Allow hooks to manipulate the template before render
self.class.callbacks_for_hook(:before_render).each do |callback|
newbody = callback.call(body, path, locs, template_class)
body = newbody if newbody # Allow the callback to return nil to skip it
end
# Read compiled template from disk or cache # Read compiled template from disk or cache
template = cache.fetch(:compiled_template, options, body) do template = cache.fetch(:compiled_template, options, body) do
::Tilt.new(path, 1, options) { body } ::Tilt.new(path, 1, options) { body }
end end
# Render using Tilt # Render using Tilt
run_hook :before_render, template.data, template content = template.render(context, path, locs, &block)
content = template.render(context, locs, &block)
run_hook :after_render, content, template # Allow hooks to manipulate the result after render
self.class.callbacks_for_hook(:after_render).each do |callback|
content = callback.call(content, locs, template_class)
end
return content return content
ensure ensure
# Reset stored buffer # Reset stored buffer
@ -408,7 +419,7 @@ module Middleman
# Find the path by searching or using the cache # Find the path by searching or using the cache
request_path = request_path.to_s request_path = request_path.to_s
cache.fetch(:resolve_template, request_path, options) do cache.fetch(:resolve_template, request_path, options) do
relative_path = request_path.sub(%r{^/}, "") relative_path = Util.strip_leading_slash(request_path)
on_disk_path = File.expand_path(relative_path, self.source_dir) on_disk_path = File.expand_path(relative_path, self.source_dir)
# By default, any engine will do # By default, any engine will do

View file

@ -194,7 +194,7 @@ module Middleman
paths.any? do |path| paths.any? do |path|
match_against.any? do |matcher| match_against.any? do |matcher|
path.match(matcher) path =~ matcher
end end
end end
end end

View file

@ -25,18 +25,18 @@ module Middleman
# Setup callbacks which can exclude paths from the sitemap # Setup callbacks which can exclude paths from the sitemap
app.config.define_setting :ignored_sitemap_matchers, { app.config.define_setting :ignored_sitemap_matchers, {
# dotfiles and folders in the root # dotfiles and folders in the root
:root_dotfiles => proc { |file| file.match(%r{^\.}) }, :root_dotfiles => proc { |file| file.start_with?('.') },
# Files starting with an dot, but not .htaccess # Files starting with an dot, but not .htaccess
:source_dotfiles => proc { |file| :source_dotfiles => proc { |file|
file.match(%r{/\.}) && !file.match(%r{/\.htaccess|\.htpasswd}) file =~ %r{/\.} && file !~ %r{/\.(htaccess|htpasswd)}
}, },
# Files starting with an underscore, but not a double-underscore # Files starting with an underscore, but not a double-underscore
:partials => proc { |file| file.match(%r{/_}) && !file.match(%r{/__}) }, :partials => proc { |file| file =~ %r{/_} && file !~ %r{/__} },
:layout => proc { |file| :layout => proc { |file|
file.match(%r{^source/layout\.}) || file.match(%r{^source/layouts/}) file.start_with?('source/layout.') || file.start_with?('source/layouts/')
} }
}, 'Callbacks that can exclude paths from the sitemap' }, 'Callbacks that can exclude paths from the sitemap'

View file

@ -111,7 +111,7 @@ module Middleman
blank_metadata = { :options => {}, :locals => {}, :page => {}, :blocks => [] } blank_metadata = { :options => {}, :locals => {}, :page => {}, :blocks => [] }
provides_metadata.inject(blank_metadata) do |result, (callback, matcher)| provides_metadata.inject(blank_metadata) do |result, (callback, matcher)|
next result if !matcher.nil? && !source_file.match(matcher) next result if matcher && !source_file.match(matcher)
metadata = callback.call(source_file) metadata = callback.call(source_file)
@ -149,9 +149,9 @@ module Middleman
@_cached_metadata[request_path] = provides_metadata_for_path.inject(blank_metadata) do |result, (callback, matcher)| @_cached_metadata[request_path] = provides_metadata_for_path.inject(blank_metadata) do |result, (callback, matcher)|
case matcher case matcher
when Regexp when Regexp
next result unless request_path.match(matcher) next result unless request_path =~ matcher
when String when String
next result unless File.fnmatch("/" + matcher.sub(%r{^/}, ''), "/#{request_path}") next result unless File.fnmatch("/" + Util.strip_leading_slash(matcher), "/#{request_path}")
end end
metadata = callback.call(request_path) metadata = callback.call(request_path)

View file

@ -10,7 +10,8 @@ require "thor"
# Core Pathname library used for traversal # Core Pathname library used for traversal
require "pathname" require "pathname"
require "rack" require "tilt"
require "rack/mime"
module Middleman module Middleman
@ -22,11 +23,19 @@ module Middleman
# @return [Boolean] # @return [Boolean]
def self.binary?(filename) def self.binary?(filename)
ext = File.extname(filename) ext = File.extname(filename)
return true if ext == '.svgz'
return false if Tilt.registered?(ext.sub('.','')) return false if Tilt.registered?(ext.sub('.',''))
ext = ".#{ext}" unless ext.to_s[0] == ?. ext = ".#{ext}" unless ext.to_s[0] == ?.
mime = ::Rack::Mime.mime_type(ext, nil) mime = ::Rack::Mime.mime_type(ext, nil)
return false unless mime unless mime
binary_bytes = [0, 1, 2, 3, 4, 5, 6, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 29, 30, 31]
s = File.read(filename, 4096) || ''
s.each_byte do |c|
return true if binary_bytes.include?(c)
end
return false
end
return false if mime.start_with?('text/') return false if mime.start_with?('text/')
return false if mime.include?('xml') return false if mime.include?('xml')
return false if mime.include?('json') return false if mime.include?('json')
@ -78,7 +87,13 @@ module Middleman
# @return [String] # @return [String]
def self.normalize_path(path) def self.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
path.sub(/^\//, "").tr('','') path.sub(%r{^/}, "").tr('','')
end
# This is a separate method from normalize_path in case we
# change how we normalize paths
def self.strip_leading_slash(path)
path.sub(%r{^/}, "")
end end
# Extract the text of a Rack response as a string. # Extract the text of a Rack response as a string.

View file

@ -26,7 +26,7 @@ Gem::Specification.new do |s|
s.add_dependency("rack-test", ["~> 0.6.1"]) s.add_dependency("rack-test", ["~> 0.6.1"])
# CLI # CLI
s.add_dependency("thor", [">= 0.15.4", "~> 0.15"]) s.add_dependency("thor", ["~> 0.15.4"])
# Helpers # Helpers
s.add_dependency("activesupport", ["~> 3.2.6"]) s.add_dependency("activesupport", ["~> 3.2.6"])

View file

@ -0,0 +1,15 @@
require 'middleman-core/util'
describe "Middleman::Util#binary?" do
%w(plain.txt unicode.txt unicode stars.svgz).each do |file|
it "recognizes #{file} as not binary" do
Middleman::Util.binary?(File.join(File.dirname(__FILE__), "binary_spec/#{file}")).should be_false
end
end
%w(middleman.png middleman).each do |file|
it "recognizes #{file} as binary" do
Middleman::Util.binary?(File.join(File.dirname(__FILE__), "binary_spec/#{file}")).should be_true
end
end
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -0,0 +1 @@
Some plain text

View file

@ -0,0 +1 @@
明日がある。

View file

@ -0,0 +1 @@
明日がある。

View file

@ -25,3 +25,18 @@ Feature: Generate mtime-based query string for busting browser caches
When I go to "/cache-buster.html" When I go to "/cache-buster.html"
Then I should see "site.css?" Then I should see "site.css?"
Then I should see "blank.gif?" Then I should see "blank.gif?"
Scenario: Rendering css with the feature and relative_assets enabled
Given "relative_assets" feature is "enabled"
Given "cache_buster" feature is "enabled"
And the Server is running at "cache-buster-app"
When I go to "/stylesheets/relative_assets.css"
Then I should see "blank.gif?"
Scenario: Rendering html with the feature and relative_assets enabled
Given "relative_assets" feature is "enabled"
Given "cache_buster" feature is "enabled"
And the Server is running at "cache-buster-app"
When I go to "/cache-buster.html"
Then I should see "site.css?"
Then I should see "blank.gif?"

View file

@ -0,0 +1,26 @@
Feature: form_tag helper
Scenario: form_tag produces relative links
Given a fixture app "indexable-app"
And an empty file named "config.rb"
And a file named "source/form_tag.html.erb" with:
"""
absolute: <% form_tag "/needs_index.html#absolute", :relative => true do %>
<% end %>
relative: <% form_tag "needs_index.html#relative", :relative => true do %>
<% end %>
"""
And a file named "source/form_tag/sub.html.erb" with:
"""
absolute: <% form_tag "/needs_index.html#absolute", :relative => true do %>
<% end %>
relative: <% form_tag "../needs_index.html#relative", :relative => true do %>
<% end %>
"""
And the Server is running at "indexable-app"
When I go to "/form_tag.html"
Then I should see 'action="needs_index.html#absolute"'
Then I should see 'action="needs_index.html#relative"'
When I go to "/form_tag/sub.html"
Then I should see 'action="../needs_index.html#absolute"'
Then I should see 'action="../needs_index.html#relative"'

View file

@ -0,0 +1,152 @@
Feature: url_for helper
Scenario: url_for produces relative links
Given a fixture app "indexable-app"
And an empty file named "config.rb"
And a file named "source/url_for.html.erb" with:
"""
absolute: <%= url_for "/needs_index.html", :relative => true %>
relative: <%= url_for "needs_index.html", :relative => true %>
"""
And a file named "source/url_for/sub.html.erb" with:
"""
absolute: <%= url_for "/needs_index.html", :relative => true %>
relative: <%= url_for "../needs_index.html", :relative => true %>
"""
And the Server is running at "indexable-app"
When I go to "/url_for.html"
Then I should see 'absolute: needs_index.html'
Then I should see 'relative: needs_index.html'
When I go to "/url_for/sub.html"
Then I should see 'absolute: ../needs_index.html'
Then I should see 'relative: ../needs_index.html'
Scenario: url_for relative works with strip_index_file
Given a fixture app "indexable-app"
And a file named "config.rb" with:
"""
set :relative_links, true
set :strip_index_file, true
helpers do
def menu_items(path='url_for.html')
sitemap.find_resource_by_destination_path(path).children
end
end
"""
And a file named "source/url_for.html.erb" with:
"""
<% menu_items.each do |item| %>
"<%= url_for(item.url) %>"
"<%= url_for(item) %>"
<% end %>
"""
And a file named "source/url_for/sub.html.erb" with:
"""
<% menu_items.each do |item| %>
"<%= url_for(item.url) %>"
"<%= url_for(item) %>"
<% end %>
"""
And the Server is running at "indexable-app"
When I go to "/url_for.html"
Then I should see '"url_for/sub.html"'
Then I should not see "/url_for/sub.html"
When I go to "/url_for/sub.html"
Then I should see '"sub.html"'
Then I should not see "/url_for/sub.html"
Scenario: url_for produces relative links when :relative_links is set to true
Given a fixture app "indexable-app"
And a file named "config.rb" with:
"""
set :relative_links, true
"""
And a file named "source/url_for.html.erb" with:
"""
absolute: <%= url_for "/needs_index.html" %>
relative: <%= url_for "needs_index.html", :relative => false %>
unknown: <%= url_for "foo.html" %>
"""
And a file named "source/url_for/sub.html.erb" with:
"""
absolute: <%= url_for "/needs_index.html" %>
relative: <%= url_for "../needs_index.html" %>
"""
And the Server is running at "indexable-app"
When I go to "/url_for.html"
Then I should see 'absolute: needs_index.html'
Then I should see 'relative: /needs_index.html'
Then I should see 'unknown: foo.html'
When I go to "/url_for/sub.html"
Then I should see 'absolute: ../needs_index.html'
Then I should see 'relative: ../needs_index.html'
Scenario: url_for knows about directory indexes
Given a fixture app "indexable-app"
And a file named "source/url_for.html.erb" with:
"""
absolute: <%= url_for "/needs_index.html", :relative => true %>
relative: <%= url_for "needs_index.html", :relative => true %>
"""
And a file named "source/url_for/sub.html.erb" with:
"""
absolute: <%= url_for "/needs_index.html", :relative => true %>
relative: <%= url_for "../needs_index.html", :relative => true %>
"""
And the Server is running at "indexable-app"
When I go to "/url_for/"
Then I should see 'absolute: ../needs_index/'
Then I should see 'relative: ../needs_index/'
When I go to "/url_for/sub/"
Then I should see 'absolute: ../../needs_index/'
Then I should see 'relative: ../../needs_index/'
Scenario: url_for can take a Resource
Given a fixture app "indexable-app"
And a file named "source/url_for.html.erb" with:
"""
"<%= url_for sitemap.find_resource_by_path("/needs_index.html") %>"
"""
And the Server is running at "indexable-app"
When I go to "/url_for/"
Then I should see '"/needs_index/"'
Scenario: Setting http_prefix
Given a fixture app "indexable-app"
And a file named "config.rb" with:
"""
set :http_prefix, "/foo"
"""
And a file named "source/url_for.html.erb" with:
"""
<%= url_for "/needs_index.html" %>
"""
And the Server is running at "indexable-app"
When I go to "/url_for.html"
Then I should see '/foo/needs_index.html'
Scenario: url_for preserves query string and anchor and isn't messed up by them
Given a fixture app "indexable-app"
And a file named "source/url_for.html.erb" with:
"""
Needs Index Anchor <%= url_for "/needs_index.html#foo" %>
Needs Index Query <%= url_for "/needs_index.html?foo" %>
Needs Index Query and Anchor <%= url_for "/needs_index.html?foo#foo" %>
"""
And the Server is running at "indexable-app"
When I go to "/url_for/"
Then I should see 'Needs Index Anchor /needs_index/#foo'
Then I should see 'Needs Index Query /needs_index/?foo'
Then I should see 'Needs Index Query and Anchor /needs_index/?foo#foo'
Scenario: url_for accepts a :query option that appends a query string
Given a fixture app "indexable-app"
And a file named "source/url_for.html.erb" with:
"""
Needs Index String <%= url_for "/needs_index.html", :query => "foo" %>
Needs Index Hash <%= url_for "/needs_index.html", :query => { :foo => :bar } %>
"""
And the Server is running at "indexable-app"
When I go to "/url_for/"
Then I should see 'Needs Index String /needs_index/?foo'
Then I should see 'Needs Index Hash /needs_index/?foo=bar'

View file

@ -104,6 +104,51 @@ Feature: i18n Preview
When I go to "/spanish/hola.html" When I go to "/spanish/hola.html"
Then I should see "Hola World" Then I should see "Hola World"
Scenario: Running localize with a non-English mount config
Given a fixture app "i18n-test-app"
And a file named "config.rb" with:
"""
activate :i18n, :mount_at_root => :es
"""
Given the Server is running at "i18n-test-app"
When I go to "/en/index.html"
Then I should see "Howdy"
When I go to "/en/hello.html"
Then I should see "Hello World"
When I go to "/"
Then I should see "Como Esta?"
When I go to "/hola.html"
Then I should see "Hola World"
When I go to "/hello.html"
Then I should see "File Not Found"
When I go to "/es/index.html"
Then I should see "File Not Found"
When I go to "/es/hola.html"
Then I should see "File Not Found"
Scenario: Running localize with a non-English lang subset
Given a fixture app "i18n-test-app"
And a file named "config.rb" with:
"""
activate :i18n, :langs => :es
"""
Given the Server is running at "i18n-test-app"
When I go to "/en/index.html"
Then I should see "File Not Found"
When I go to "/en/hello.html"
Then I should see "File Not Found"
When I go to "/"
Then I should see "Como Esta?"
When I go to "/hola.html"
Then I should see "Hola World"
When I go to "/hello.html"
Then I should see "File Not Found"
When I go to "/es/index.html"
Then I should see "File Not Found"
When I go to "/es/hola.html"
Then I should see "File Not Found"
Scenario: Running localize with the no mount config Scenario: Running localize with the no mount config
Given a fixture app "i18n-test-app" Given a fixture app "i18n-test-app"
And a file named "config.rb" with: And a file named "config.rb" with:

View file

@ -64,7 +64,7 @@ module Middleman
# If the basename of the request as no extension, assume we are serving a # If the basename of the request as no extension, assume we are serving a
# directory and join index_file to the path. # directory and join index_file to the path.
path = File.join(asset_dir, current_path) path = File.join(asset_dir, current_path)
path = path.gsub(File.extname(path), ".#{asset_ext}") path = path.sub(/#{File.extname(path)}$/, ".#{asset_ext}")
yield path if sitemap.find_resource_by_path(path) yield path if sitemap.find_resource_by_path(path)
end end
@ -74,11 +74,11 @@ module Middleman
# @return [String] # @return [String]
def page_classes def page_classes
path = current_path.dup path = current_path.dup
path << index_file if path.match(%r{/$}) path << index_file if path.end_with?('/')
path = path.gsub(%r{^/}, '') path = Util.strip_leading_slash(path)
classes = [] classes = []
parts = path.split('.')[0].split('/') parts = path.split('.').first.split('/')
parts.each_with_index { |path, i| classes << parts.first(i+1).join('_') } parts.each_with_index { |path, i| classes << parts.first(i+1).join('_') }
classes.join(' ') classes.join(' ')
@ -90,22 +90,93 @@ module Middleman
# @param [String] source The path to the file # @param [String] source The path to the file
# @return [String] # @return [String]
def asset_path(kind, source) def asset_path(kind, source)
return source if source =~ /^http/ return source if source.to_s.include?('//')
asset_folder = case kind asset_folder = case kind
when :css then css_dir when :css then css_dir
when :js then js_dir when :js then js_dir
when :images then images_dir when :images then images_dir
else kind.to_s else kind.to_s
end end
source = source.to_s.gsub(/\s/, '') source = source.to_s.tr(' ', '')
ignore_extension = (kind == :images) # don't append extension ignore_extension = (kind == :images) # don't append extension
source << ".#{kind}" unless ignore_extension or source =~ /\.#{kind}/ source << ".#{kind}" unless ignore_extension || source.end_with?(".#{kind}")
if source =~ %r{^/} # absolute path asset_folder = "" if source.start_with?('/') # absolute path
asset_folder = ""
end
asset_url(source, asset_folder) asset_url(source, asset_folder)
end end
# Given a source path (referenced either absolutely or relatively)
# or a Resource, this will produce the nice URL configured for that
# path, respecting :relative_links, directory indexes, etc.
def url_for(path_or_resource, options={})
# Handle Resources and other things which define their own url method
url = path_or_resource.respond_to?(:url) ? path_or_resource.url : path_or_resource
begin
uri = URI(url)
rescue URI::InvalidURIError
# Nothing we can do with it, it's not really a URI
return url
end
relative = options.delete(:relative)
raise "Can't use the relative option with an external URL" if relative && uri.host
# Allow people to turn on relative paths for all links with
# set :relative_links, true
# but still override on a case by case basis with the :relative parameter.
effective_relative = relative || false
effective_relative = true if relative.nil? && config[:relative_links]
# Try to find a sitemap resource corresponding to the desired path
this_resource = current_resource # store in a local var to save work
if path_or_resource.is_a?(Sitemap::Resource)
resource = path_or_resource
resource_url = url
elsif this_resource && uri.path
# Handle relative urls
url_path = Pathname(uri.path)
current_source_dir = Pathname('/' + this_resource.path).dirname
url_path = current_source_dir.join(url_path) if url_path.relative?
resource = sitemap.find_resource_by_path(url_path.to_s)
resource_url = resource.url if resource
end
if resource
# Switch to the relative path between this_resource and the given resource
# if we've been asked to.
if effective_relative
# Output urls relative to the destination path, not the source path
current_dir = Pathname('/' + this_resource.destination_path).dirname
relative_path = Pathname(resource_url).relative_path_from(current_dir).to_s
# Put back the trailing slash to avoid unnecessary Apache redirects
if resource_url.end_with?('/') && !relative_path.end_with?('/')
relative_path << '/'
end
uri.path = relative_path
else
uri.path = resource_url
end
else
# If they explicitly asked for relative links but we can't find a resource...
raise "No resource exists at #{url}" if relative
end
# Support a :query option that can be a string or hash
if query = options.delete(:query)
uri.query = query.respond_to?(:to_param) ? query.to_param : query.to_s
end
# Support a :fragment or :anchor option just like Padrino
fragment = options.delete(:anchor) || options.delete(:fragment)
uri.fragment = fragment.to_s if fragment
# Finally make the URL back into a string
uri.to_s
end
# Overload the regular link_to to be sitemap-aware - if you # Overload the regular link_to to be sitemap-aware - if you
# reference a source path, either absolutely or relatively, # reference a source path, either absolutely or relatively,
# you'll get that resource's nice URL. Also, there is a # you'll get that resource's nice URL. Also, there is a
@ -123,75 +194,27 @@ module Middleman
url_arg_index = block_given? ? 0 : 1 url_arg_index = block_given? ? 0 : 1
options_index = block_given? ? 1 : 2 options_index = block_given? ? 1 : 2
if block_given? && args.size > 2
raise ArgumentError.new("Too many arguments to link_to(url, options={}, &block)")
end
if url = args[url_arg_index] if url = args[url_arg_index]
options = args[options_index] || {} options = args[options_index] || {}
relative = options.delete(:relative) raise ArgumentError.new("Options must be a hash") unless options.is_a?(Hash)
# Handle Resources, which define their own url method # Transform the url through our magic url_for method
if url.respond_to? :url args[url_arg_index] = url_for(url, options)
url = args[url_arg_index] = url.url
end
if url.include? '://'
raise "Can't use the relative option with an external URL" if relative
elsif current_resource
# Handle relative urls
current_source_dir = Pathname('/' + current_resource.path).dirname
begin
uri = URI(url)
url_path = uri.path
rescue
end
if url_path
path = Pathname(url_path)
url_path = current_source_dir.join(path).to_s if path.relative?
resource = sitemap.find_resource_by_path(url_path)
# Allow people to turn on relative paths for all links with config[:relative_links] = true
# but still override on a case by case basis with the :relative parameter.
effective_relative = relative || false
if relative.nil? && config[:relative_links]
effective_relative = true
end
if resource
if effective_relative
resource_url = resource.url
# Output urls relative to the destination path, not the source path
current_dir = Pathname('/' + current_resource.destination_path).dirname
new_url = Pathname(resource_url).relative_path_from(current_dir).to_s
# Put back the trailing slash to avoid unnecessary Apache redirects
if resource_url.end_with?('/') && !new_url.end_with?('/')
new_url << '/'
end
else
new_url = resource.url
end
uri.path = new_url
args[url_arg_index] = uri.to_s
else
raise "No resource exists at #{url}" if relative
end
end
end
# Support a :query option that can be a string or hash
if query = options.delete(:query)
uri = URI(args[url_arg_index])
uri.query = query.respond_to?(:to_param) ? query.to_param : query.to_s
args[url_arg_index] = uri.to_s
end
end end
super(*args, &block) super(*args, &block)
end end
# Modified Padrino form_for that uses Middleman's url_for
# to transform the URL.
def form_tag(url, options={}, &block)
url = url_for(url, options)
super
end
end end
end end
end end

View file

@ -28,6 +28,7 @@ module Middleman
@app = app @app = app
@locales_glob = File.join(app.locales_dir, "**", "*.{rb,yml,yaml}") @locales_glob = File.join(app.locales_dir, "**", "*.{rb,yml,yaml}")
# File.fnmatch doesn't support brackets: {rb,yml,yaml}
regex = @locales_glob.sub(/\./, '\.').sub(File.join("**", "*"), ".*").sub(/\//, '\/').sub("{rb,yml,yaml}", "rb|ya?ml") regex = @locales_glob.sub(/\./, '\.').sub(File.join("**", "*"), ".*").sub(/\//, '\/').sub("{rb,yml,yaml}", "rb|ya?ml")
@locales_regex = %r{^#{regex}} @locales_regex = %r{^#{regex}}
@ -43,7 +44,7 @@ module Middleman
@mount_at_root = @options.has_key?(:mount_at_root) ? @options[:mount_at_root] : langs.first @mount_at_root = @options.has_key?(:mount_at_root) ? @options[:mount_at_root] : langs.first
if !@app.build? if !@app.build?
logger.info "== Locales: #{langs.join(", ")}" logger.info "== Locales: #{langs.join(", ")} (Default #{@mount_at_root})"
end end
# Don't output localizable files # Don't output localizable files
@ -52,15 +53,20 @@ module Middleman
@app.sitemap.provides_metadata_for_path do |url| @app.sitemap.provides_metadata_for_path do |url|
if d = get_localization_data(url) if d = get_localization_data(url)
lang, page_id = d lang, page_id = d
instance_vars = Proc.new { else
# Default to the @mount_at_root lang
page_id = nil
lang = @mount_at_root
end
instance_vars = Proc.new do
::I18n.locale = lang ::I18n.locale = lang
@lang = lang @lang = lang
@page_id = page_id @page_id = page_id
}
{ :blocks => [instance_vars] }
else
{}
end end
locals = { :lang => lang, :page_id => page_id }
{ :blocks => [instance_vars], :locals => locals }
end end
@app.sitemap.register_resource_list_manipulator( @app.sitemap.register_resource_list_manipulator(
@ -73,13 +79,15 @@ module Middleman
end end
def on_file_changed(file) def on_file_changed(file)
if @locales_regex.match(file) if @locales_regex =~ file
::I18n.reload! ::I18n.reload!
end end
end end
def langs def langs
@options[:langs] || begin if @options[:langs]
Array(@options[:langs]).map(&:to_sym)
else
Dir[File.join(@app.root, @locales_glob)].map { |file| Dir[File.join(@app.root, @locales_glob)].map { |file|
File.basename(file).sub(/\.ya?ml$/, "").sub(/\.rb$/, "") File.basename(file).sub(/\.ya?ml$/, "").sub(/\.rb$/, "")
}.sort.map(&:to_sym) }.sort.map(&:to_sym)

View file

@ -38,7 +38,7 @@ module Middleman
params[:alt] ||= "" params[:alt] ||= ""
real_path = path real_path = path
real_path = File.join(images_dir, real_path) unless real_path =~ %r{^/} real_path = File.join(images_dir, real_path) unless real_path.start_with?('/')
full_path = File.join(source_dir, real_path) full_path = File.join(source_dir, real_path)
if File.exists?(full_path) if File.exists?(full_path)

View file

@ -36,7 +36,7 @@ module Middleman
path path
else else
current_dir = Pathname('/' + current_resource.destination_path) current_dir = Pathname('/' + current_resource.destination_path)
Pathname(path).relative_path_from(current_dir.dirname) Pathname(path).relative_path_from(current_dir.dirname).to_s
end end
end end
end end

2
vendor.yml Normal file
View file

@ -0,0 +1,2 @@
- fixtures
- templates